security fix: replaced some strcat with strncat
[xboard.git] / xboard.c
1 /*
2  * xboard.c -- X front end for XBoard
3  *
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,
5  * Massachusetts.
6  *
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8  * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
9  *
10  * The following terms apply to Digital Equipment Corporation's copyright
11  * interest in XBoard:
12  * ------------------------------------------------------------------------
13  * All Rights Reserved
14  *
15  * Permission to use, copy, modify, and distribute this software and its
16  * documentation for any purpose and without fee is hereby granted,
17  * provided that the above copyright notice appear in all copies and that
18  * both that copyright notice and this permission notice appear in
19  * supporting documentation, and that the name of Digital not be
20  * used in advertising or publicity pertaining to distribution of the
21  * software without specific, written prior permission.
22  *
23  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29  * SOFTWARE.
30  * ------------------------------------------------------------------------
31  *
32  * The following terms apply to the enhanced version of XBoard
33  * distributed by the Free Software Foundation:
34  * ------------------------------------------------------------------------
35  *
36  * GNU XBoard is free software: you can redistribute it and/or modify
37  * it under the terms of the GNU General Public License as published by
38  * the Free Software Foundation, either version 3 of the License, or (at
39  * your option) any later version.
40  *
41  * GNU XBoard is distributed in the hope that it will be useful, but
42  * WITHOUT ANY WARRANTY; without even the implied warranty of
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44  * General Public License for more details.
45  *
46  * You should have received a copy of the GNU General Public License
47  * along with this program. If not, see http://www.gnu.org/licenses/.  *
48  *
49  *------------------------------------------------------------------------
50  ** See the file ChangeLog for a revision history.  */
51
52 #define HIGHDRAG 1
53
54 #include "config.h"
55
56 #include <stdio.h>
57 #include <ctype.h>
58 #include <signal.h>
59 #include <errno.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <pwd.h>
63
64 #if !OMIT_SOCKETS
65 # if HAVE_SYS_SOCKET_H
66 #  include <sys/socket.h>
67 #  include <netinet/in.h>
68 #  include <netdb.h>
69 # else /* not HAVE_SYS_SOCKET_H */
70 #  if HAVE_LAN_SOCKET_H
71 #   include <lan/socket.h>
72 #   include <lan/in.h>
73 #   include <lan/netdb.h>
74 #  else /* not HAVE_LAN_SOCKET_H */
75 #   define OMIT_SOCKETS 1
76 #  endif /* not HAVE_LAN_SOCKET_H */
77 # endif /* not HAVE_SYS_SOCKET_H */
78 #endif /* !OMIT_SOCKETS */
79
80 #if STDC_HEADERS
81 # include <stdlib.h>
82 # include <string.h>
83 #else /* not STDC_HEADERS */
84 extern char *getenv();
85 # if HAVE_STRING_H
86 #  include <string.h>
87 # else /* not HAVE_STRING_H */
88 #  include <strings.h>
89 # endif /* not HAVE_STRING_H */
90 #endif /* not STDC_HEADERS */
91
92 #if HAVE_SYS_FCNTL_H
93 # include <sys/fcntl.h>
94 #else /* not HAVE_SYS_FCNTL_H */
95 # if HAVE_FCNTL_H
96 #  include <fcntl.h>
97 # endif /* HAVE_FCNTL_H */
98 #endif /* not HAVE_SYS_FCNTL_H */
99
100 #if HAVE_SYS_SYSTEMINFO_H
101 # include <sys/systeminfo.h>
102 #endif /* HAVE_SYS_SYSTEMINFO_H */
103
104 #if TIME_WITH_SYS_TIME
105 # include <sys/time.h>
106 # include <time.h>
107 #else
108 # if HAVE_SYS_TIME_H
109 #  include <sys/time.h>
110 # else
111 #  include <time.h>
112 # endif
113 #endif
114
115 #if HAVE_UNISTD_H
116 # include <unistd.h>
117 #endif
118
119 #if HAVE_SYS_WAIT_H
120 # include <sys/wait.h>
121 #endif
122
123 #if HAVE_DIRENT_H
124 # include <dirent.h>
125 # define NAMLEN(dirent) strlen((dirent)->d_name)
126 # define HAVE_DIR_STRUCT
127 #else
128 # define dirent direct
129 # define NAMLEN(dirent) (dirent)->d_namlen
130 # if HAVE_SYS_NDIR_H
131 #  include <sys/ndir.h>
132 #  define HAVE_DIR_STRUCT
133 # endif
134 # if HAVE_SYS_DIR_H
135 #  include <sys/dir.h>
136 #  define HAVE_DIR_STRUCT
137 # endif
138 # if HAVE_NDIR_H
139 #  include <ndir.h>
140 #  define HAVE_DIR_STRUCT
141 # endif
142 #endif
143
144 #include <X11/Intrinsic.h>
145 #include <X11/StringDefs.h>
146 #include <X11/Shell.h>
147 #include <X11/cursorfont.h>
148 #include <X11/Xatom.h>
149 #include <X11/Xmu/Atoms.h>
150 #if USE_XAW3D
151 #include <X11/Xaw3d/Dialog.h>
152 #include <X11/Xaw3d/Form.h>
153 #include <X11/Xaw3d/List.h>
154 #include <X11/Xaw3d/Label.h>
155 #include <X11/Xaw3d/SimpleMenu.h>
156 #include <X11/Xaw3d/SmeBSB.h>
157 #include <X11/Xaw3d/SmeLine.h>
158 #include <X11/Xaw3d/Box.h>
159 #include <X11/Xaw3d/MenuButton.h>
160 #include <X11/Xaw3d/Text.h>
161 #include <X11/Xaw3d/AsciiText.h>
162 #else
163 #include <X11/Xaw/Dialog.h>
164 #include <X11/Xaw/Form.h>
165 #include <X11/Xaw/List.h>
166 #include <X11/Xaw/Label.h>
167 #include <X11/Xaw/SimpleMenu.h>
168 #include <X11/Xaw/SmeBSB.h>
169 #include <X11/Xaw/SmeLine.h>
170 #include <X11/Xaw/Box.h>
171 #include <X11/Xaw/MenuButton.h>
172 #include <X11/Xaw/Text.h>
173 #include <X11/Xaw/AsciiText.h>
174 #endif
175
176 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
177 #include "common.h"
178
179 #if HAVE_LIBXPM
180 #include <X11/xpm.h>
181 #include "pixmaps/pixmaps.h"
182 #define IMAGE_EXT "xpm"
183 #else
184 #define IMAGE_EXT "xim"
185 #include "bitmaps/bitmaps.h"
186 #endif
187
188 #include "bitmaps/icon_white.bm"
189 #include "bitmaps/icon_black.bm"
190 #include "bitmaps/checkmark.bm"
191
192 #include "frontend.h"
193 #include "backend.h"
194 #include "backendz.h"
195 #include "moves.h"
196 #include "xboard.h"
197 #include "childio.h"
198 #include "xgamelist.h"
199 #include "xhistory.h"
200 #include "xedittags.h"
201 #include "gettext.h"
202
203 // must be moved to xengineoutput.h
204
205 void EngineOutputProc P((Widget w, XEvent *event,
206                          String *prms, Cardinal *nprms));
207 void EvalGraphProc P((Widget w, XEvent *event,
208                       String *prms, Cardinal *nprms));
209
210
211 #ifdef __EMX__
212 #ifndef HAVE_USLEEP
213 #define HAVE_USLEEP
214 #endif
215 #define usleep(t)   _sleep2(((t)+500)/1000)
216 #endif
217
218 #ifdef ENABLE_NLS
219 # define  _(s) gettext (s)
220 # define N_(s) gettext_noop (s)
221 #else
222 # define  _(s) (s)
223 # define N_(s)  s
224 #endif
225
226 typedef struct {
227     String string;
228     XtActionProc proc;
229 } MenuItem;
230
231 typedef struct {
232     String name;
233     MenuItem *mi;
234 } Menu;
235
236 int main P((int argc, char **argv));
237 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
238                 char *init_path, char *mode, int (*show_entry)(), char **name_return));
239 RETSIGTYPE CmailSigHandler P((int sig));
240 RETSIGTYPE IntSigHandler P((int sig));
241 RETSIGTYPE TermSizeSigHandler P((int sig));
242 void CreateGCs P((void));
243 void CreateXIMPieces P((void));
244 void CreateXPMPieces P((void));
245 void CreatePieces P((void));
246 void CreatePieceMenus P((void));
247 Widget CreateMenuBar P((Menu *mb));
248 Widget CreateButtonBar P ((MenuItem *mi));
249 char *FindFont P((char *pattern, int targetPxlSize));
250 void PieceMenuPopup P((Widget w, XEvent *event,
251                        String *params, Cardinal *num_params));
252 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
253 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
254 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
255                    u_int wreq, u_int hreq));
256 void CreateGrid P((void));
257 int EventToSquare P((int x, int limit));
258 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
259 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
260 void HandleUserMove P((Widget w, XEvent *event,
261                      String *prms, Cardinal *nprms));
262 void AnimateUserMove P((Widget w, XEvent * event,
263                      String * params, Cardinal * nParams));
264 void HandlePV P((Widget w, XEvent * event,
265                      String * params, Cardinal * nParams));
266 void SelectPV P((Widget w, XEvent * event,
267                      String * params, Cardinal * nParams));
268 void StopPV P((Widget w, XEvent * event,
269                      String * params, Cardinal * nParams));
270 void WhiteClock P((Widget w, XEvent *event,
271                    String *prms, Cardinal *nprms));
272 void BlackClock P((Widget w, XEvent *event,
273                    String *prms, Cardinal *nprms));
274 void DrawPositionProc P((Widget w, XEvent *event,
275                      String *prms, Cardinal *nprms));
276 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
277                      Board board));
278 void CommentClick P((Widget w, XEvent * event,
279                    String * params, Cardinal * nParams));
280 void CommentPopUp P((char *title, char *label));
281 void CommentPopDown P((void));
282 void CommentCallback P((Widget w, XtPointer client_data,
283                         XtPointer call_data));
284 void ICSInputBoxPopUp P((void));
285 void ICSInputBoxPopDown P((void));
286 void FileNamePopUp P((char *label, char *def,
287                       FileProc proc, char *openMode));
288 void FileNamePopDown P((void));
289 void FileNameCallback P((Widget w, XtPointer client_data,
290                          XtPointer call_data));
291 void FileNameAction P((Widget w, XEvent *event,
292                        String *prms, Cardinal *nprms));
293 void AskQuestionReplyAction P((Widget w, XEvent *event,
294                           String *prms, Cardinal *nprms));
295 void AskQuestionProc P((Widget w, XEvent *event,
296                           String *prms, Cardinal *nprms));
297 void AskQuestionPopDown P((void));
298 void PromotionPopDown P((void));
299 void PromotionCallback P((Widget w, XtPointer client_data,
300                           XtPointer call_data));
301 void EditCommentPopDown P((void));
302 void EditCommentCallback P((Widget w, XtPointer client_data,
303                             XtPointer call_data));
304 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
305 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
306 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
307 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
308                          Cardinal *nprms));
309 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
310                          Cardinal *nprms));
311 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
312                        Cardinal *nprms));
313 void LoadPositionProc P((Widget w, XEvent *event,
314                          String *prms, Cardinal *nprms));
315 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
316                          Cardinal *nprms));
317 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
318                          Cardinal *nprms));
319 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
320                        Cardinal *nprms));
321 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
322                          Cardinal *nprms));
323 void PastePositionProc P((Widget w, XEvent *event, String *prms,
324                           Cardinal *nprms));
325 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
326 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
327 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
328 void SavePositionProc P((Widget w, XEvent *event,
329                          String *prms, Cardinal *nprms));
330 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
331 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
332                             Cardinal *nprms));
333 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
334 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
335 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
336                          Cardinal *nprms));
337 void MachineWhiteProc P((Widget w, XEvent *event,
338                          String *prms, Cardinal *nprms));
339 void AnalyzeModeProc P((Widget w, XEvent *event,
340                          String *prms, Cardinal *nprms));
341 void AnalyzeFileProc P((Widget w, XEvent *event,
342                          String *prms, Cardinal *nprms));
343 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
344                         Cardinal *nprms));
345 void IcsClientProc P((Widget w, XEvent *event, String *prms,
346                       Cardinal *nprms));
347 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
348 void EditPositionProc P((Widget w, XEvent *event,
349                          String *prms, Cardinal *nprms));
350 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
351 void EditCommentProc P((Widget w, XEvent *event,
352                         String *prms, Cardinal *nprms));
353 void IcsInputBoxProc P((Widget w, XEvent *event,
354                         String *prms, Cardinal *nprms));
355 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
356 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
357 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
358 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
359 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
362 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
363 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
365 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
367 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
368 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void StopObservingProc P((Widget w, XEvent *event, String *prms,
370                           Cardinal *nprms));
371 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
372                           Cardinal *nprms));
373 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
381                          Cardinal *nprms));
382 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
383                         Cardinal *nprms));
384 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
385 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
386                         Cardinal *nprms));
387 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
388                          Cardinal *nprms));
389 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
390                          Cardinal *nprms));
391 void AutocommProc P((Widget w, XEvent *event, String *prms,
392                      Cardinal *nprms));
393 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
395 void AutobsProc P((Widget w, XEvent *event, String *prms,
396                         Cardinal *nprms));
397 void AutoraiseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
398 void AutosaveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
399 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
400 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
401                        Cardinal *nprms));
402 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
403 void GetMoveListProc P((Widget w, XEvent *event, String *prms,
404                         Cardinal *nprms));
405 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
406                               Cardinal *nprms));
407 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
408                               Cardinal *nprms));
409 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
410 void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
411 void OldSaveStyleProc P((Widget w, XEvent *event, String *prms,
412                          Cardinal *nprms));
413 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
414                          Cardinal *nprms));
415 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
416                            Cardinal *nprms));
417 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
418                         Cardinal *nprms));
419 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
420                              Cardinal *nprms));
421 void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
422 void QuietPlayProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
423 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
424                        Cardinal *nprms));
425 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
426                          Cardinal *nprms));
427 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
428                          Cardinal *nprms));
429 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
430                           Cardinal *nprms));
431 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
432 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
433 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
434 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
435 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
436 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
437 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
438 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
439 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
440 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
441 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
442 void DisplayMove P((int moveNumber));
443 void DisplayTitle P((char *title));
444 void ICSInitScript P((void));
445 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
446 void ErrorPopUp P((char *title, char *text, int modal));
447 void ErrorPopDown P((void));
448 static char *ExpandPathName P((char *path));
449 static void CreateAnimVars P((void));
450 static void DragPieceMove P((int x, int y));
451 static void DrawDragPiece P((void));
452 char *ModeToWidgetName P((GameMode mode));
453 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
454 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
455 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
456 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
457 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
458 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
459 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void GameListOptionsPopDown P(());
462 void ShufflePopDown P(());
463 void EnginePopDown P(());
464 void UciPopDown P(());
465 void TimeControlPopDown P(());
466 void NewVariantPopDown P(());
467 void SettingsPopDown P(());
468 void update_ics_width P(());
469 int get_term_width P(());
470 int CopyMemoProc P(());
471 /*
472 * XBoard depends on Xt R4 or higher
473 */
474 int xtVersion = XtSpecificationRelease;
475
476 int xScreen;
477 Display *xDisplay;
478 Window xBoardWindow;
479 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
480   jailSquareColor, highlightSquareColor, premoveHighlightColor;
481 Pixel lowTimeWarningColor;
482 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
483   bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
484   wjPieceGC, bjPieceGC, prelineGC, countGC;
485 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
486 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
487   whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
488   commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
489   menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
490   ICSInputShell, fileNameShell, askQuestionShell;
491 Widget historyShell, evalGraphShell, gameListShell;
492 int hOffset; // [HGM] dual
493 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
494 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
495 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
496 Font clockFontID, coordFontID, countFontID;
497 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
498 XtAppContext appContext;
499 char *layoutName;
500 char *oldICSInteractionTitle;
501
502 FileProc fileProc;
503 char *fileOpenMode;
504 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
505
506 Position commentX = -1, commentY = -1;
507 Dimension commentW, commentH;
508 typedef unsigned int BoardSize;
509 BoardSize boardSize;
510 Boolean chessProgram;
511
512 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
513 int squareSize, smallLayout = 0, tinyLayout = 0,
514   marginW, marginH, // [HGM] for run-time resizing
515   fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
516   ICSInputBoxUp = False, askQuestionUp = False,
517   filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
518   editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
519 Pixel timerForegroundPixel, timerBackgroundPixel;
520 Pixel buttonForegroundPixel, buttonBackgroundPixel;
521 char *chessDir, *programName, *programVersion,
522   *gameCopyFilename, *gamePasteFilename;
523 Boolean alwaysOnTop = False;
524 Boolean saveSettingsOnExit;
525 char *settingsFileName;
526 char *icsTextMenuString;
527 char *icsNames;
528 char *firstChessProgramNames;
529 char *secondChessProgramNames;
530
531 WindowPlacement wpMain;
532 WindowPlacement wpConsole;
533 WindowPlacement wpComment;
534 WindowPlacement wpMoveHistory;
535 WindowPlacement wpEvalGraph;
536 WindowPlacement wpEngineOutput;
537 WindowPlacement wpGameList;
538 WindowPlacement wpTags;
539
540 #define SOLID 0
541 #define OUTLINE 1
542 Pixmap pieceBitmap[2][(int)BlackPawn];
543 Pixmap pieceBitmap2[2][(int)BlackPawn+4];       /* [HGM] pieces */
544 Pixmap xpmPieceBitmap[4][(int)BlackPawn];       /* LL, LD, DL, DD actually used*/
545 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4];    /* LL, LD, DL, DD set to select from */
546 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
547 int useImages, useImageSqs;
548 XImage *ximPieceBitmap[4][(int)BlackPawn+4];    /* LL, LD, DL, DD */
549 Pixmap ximMaskPm[(int)BlackPawn];               /* clipmasks, used for XIM pieces */
550 Pixmap ximMaskPm2[(int)BlackPawn+4];            /* clipmasks, used for XIM pieces */
551 XImage *ximLightSquare, *ximDarkSquare;
552 XImage *xim_Cross;
553
554 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
555 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
556
557 #define White(piece) ((int)(piece) < (int)BlackPawn)
558
559 /* Variables for doing smooth animation. This whole thing
560    would be much easier if the board was double-buffered,
561    but that would require a fairly major rewrite.       */
562
563 typedef struct {
564         Pixmap  saveBuf;
565         Pixmap  newBuf;
566         GC      blitGC, pieceGC, outlineGC;
567         XPoint  startSquare, prevFrame, mouseDelta;
568         int     startColor;
569         int     dragPiece;
570         Boolean dragActive;
571         int     startBoardX, startBoardY;
572     } AnimState;
573
574 /* There can be two pieces being animated at once: a player
575    can begin dragging a piece before the remote opponent has moved. */
576
577 static AnimState game, player;
578
579 /* Bitmaps for use as masks when drawing XPM pieces.
580    Need one for each black and white piece.             */
581 static Pixmap xpmMask[BlackKing + 1];
582
583 /* This magic number is the number of intermediate frames used
584    in each half of the animation. For short moves it's reduced
585    by 1. The total number of frames will be factor * 2 + 1.  */
586 #define kFactor    4
587
588 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
589
590 MenuItem fileMenu[] = {
591     {N_("New Game"), ResetProc},
592     {N_("New Shuffle Game ..."), ShuffleMenuProc},
593     {N_("New Variant ..."), NewVariantProc},      // [HGM] variant: not functional yet
594     {"----", NothingProc},
595     {N_("Load Game"), LoadGameProc},
596     {N_("Load Next Game"), LoadNextGameProc},
597     {N_("Load Previous Game"), LoadPrevGameProc},
598     {N_("Reload Same Game"), ReloadGameProc},
599     {N_("Save Game"), SaveGameProc},
600     {"----", NothingProc},
601     {N_("Copy Game"), CopyGameProc},
602     {N_("Paste Game"), PasteGameProc},
603     {"----", NothingProc},
604     {N_("Load Position"), LoadPositionProc},
605     {N_("Load Next Position"), LoadNextPositionProc},
606     {N_("Load Previous Position"), LoadPrevPositionProc},
607     {N_("Reload Same Position"), ReloadPositionProc},
608     {N_("Save Position"), SavePositionProc},
609     {"----", NothingProc},
610     {N_("Copy Position"), CopyPositionProc},
611     {N_("Paste Position"), PastePositionProc},
612     {"----", NothingProc},
613     {N_("Mail Move"), MailMoveProc},
614     {N_("Reload CMail Message"), ReloadCmailMsgProc},
615     {"----", NothingProc},
616     {N_("Exit"), QuitProc},
617     {NULL, NULL}
618 };
619
620 MenuItem modeMenu[] = {
621     {N_("Machine White"), MachineWhiteProc},
622     {N_("Machine Black"), MachineBlackProc},
623     {N_("Two Machines"), TwoMachinesProc},
624     {N_("Analysis Mode"), AnalyzeModeProc},
625     {N_("Analyze File"), AnalyzeFileProc },
626     {N_("ICS Client"), IcsClientProc},
627     {N_("Edit Game"), EditGameProc},
628     {N_("Edit Position"), EditPositionProc},
629     {N_("Training"), TrainingProc},
630     {"----", NothingProc},
631     {N_("Show Engine Output"), EngineOutputProc},
632     {N_("Show Evaluation Graph"), EvalGraphProc},
633     {N_("Show Game List"), ShowGameListProc},
634     {N_("Show Move History"), HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
635     {"----", NothingProc},
636     {N_("Edit Tags"), EditTagsProc},
637     {N_("Edit Comment"), EditCommentProc},
638     {N_("ICS Input Box"), IcsInputBoxProc},
639     {N_("Pause"), PauseProc},
640     {NULL, NULL}
641 };
642
643 MenuItem actionMenu[] = {
644     {N_("Accept"), AcceptProc},
645     {N_("Decline"), DeclineProc},
646     {N_("Rematch"), RematchProc},
647     {"----", NothingProc},
648     {N_("Call Flag"), CallFlagProc},
649     {N_("Draw"), DrawProc},
650     {N_("Adjourn"), AdjournProc},
651     {N_("Abort"), AbortProc},
652     {N_("Resign"), ResignProc},
653     {"----", NothingProc},
654     {N_("Stop Observing"), StopObservingProc},
655     {N_("Stop Examining"), StopExaminingProc},
656     {N_("Upload to Examine"), UploadProc},
657     {"----", NothingProc},
658     {N_("Adjudicate to White"), AdjuWhiteProc},
659     {N_("Adjudicate to Black"), AdjuBlackProc},
660     {N_("Adjudicate Draw"), AdjuDrawProc},
661     {NULL, NULL}
662 };
663
664 MenuItem stepMenu[] = {
665     {N_("Backward"), BackwardProc},
666     {N_("Forward"), ForwardProc},
667     {N_("Back to Start"), ToStartProc},
668     {N_("Forward to End"), ToEndProc},
669     {N_("Revert"), RevertProc},
670     {N_("Annotate"), AnnotateProc},
671     {N_("Truncate Game"), TruncateGameProc},
672     {"----", NothingProc},
673     {N_("Move Now"), MoveNowProc},
674     {N_("Retract Move"), RetractMoveProc},
675     {NULL, NULL}
676 };
677
678 MenuItem optionsMenu[] = {
679     {N_("Flip View"), FlipViewProc},
680     {"----", NothingProc},
681     {N_("Adjudications ..."), EngineMenuProc},
682     {N_("General Settings ..."), UciMenuProc},
683     {N_("Engine #1 Settings ..."), FirstSettingsProc},
684     {N_("Engine #2 Settings ..."), SecondSettingsProc},
685     {N_("Time Control ..."), TimeControlProc},
686     {N_("Game List ..."), GameListOptionsPopUp},
687     {"----", NothingProc},
688     {N_("Always Queen"), AlwaysQueenProc},
689     {N_("Animate Dragging"), AnimateDraggingProc},
690     {N_("Animate Moving"), AnimateMovingProc},
691     {N_("Auto Comment"), AutocommProc},
692     {N_("Auto Flag"), AutoflagProc},
693     {N_("Auto Flip View"), AutoflipProc},
694     {N_("Auto Observe"), AutobsProc},
695     {N_("Auto Raise Board"), AutoraiseProc},
696     {N_("Auto Save"), AutosaveProc},
697     {N_("Blindfold"), BlindfoldProc},
698     {N_("Flash Moves"), FlashMovesProc},
699     {N_("Get Move List"), GetMoveListProc},
700 #if HIGHDRAG
701     {N_("Highlight Dragging"), HighlightDraggingProc},
702 #endif
703     {N_("Highlight Last Move"), HighlightLastMoveProc},
704     {N_("Move Sound"), MoveSoundProc},
705     {N_("ICS Alarm"), IcsAlarmProc},
706     {N_("Old Save Style"), OldSaveStyleProc},
707     {N_("Periodic Updates"), PeriodicUpdatesProc},
708     {N_("Ponder Next Move"), PonderNextMoveProc},
709     {N_("Popup Exit Message"), PopupExitMessageProc},
710     {N_("Popup Move Errors"), PopupMoveErrorsProc},
711     {N_("Premove"), PremoveProc},
712     {N_("Quiet Play"), QuietPlayProc},
713     {N_("Show Coords"), ShowCoordsProc},
714     {N_("Hide Thinking"), HideThinkingProc},
715     {N_("Test Legality"), TestLegalityProc},
716     {"----", NothingProc},
717     {N_("Save Settings Now"), SaveSettingsProc},
718     {N_("Save Settings on Exit"), SaveOnExitProc},
719     {NULL, NULL}
720 };
721
722 MenuItem helpMenu[] = {
723     {N_("Info XBoard"), InfoProc},
724     {N_("Man XBoard"), ManProc},
725     {"----", NothingProc},
726     {N_("Hint"), HintProc},
727     {N_("Book"), BookProc},
728     {"----", NothingProc},
729     {N_("About XBoard"), AboutProc},
730     {NULL, NULL}
731 };
732
733 Menu menuBar[] = {
734     {N_("File"), fileMenu},
735     {N_("Mode"), modeMenu},
736     {N_("Action"), actionMenu},
737     {N_("Step"), stepMenu},
738     {N_("Options"), optionsMenu},
739     {N_("Help"), helpMenu},
740     {NULL, NULL}
741 };
742
743 #define PAUSE_BUTTON N_("P")
744 MenuItem buttonBar[] = {
745     {"<<", ToStartProc},
746     {"<", BackwardProc},
747     {PAUSE_BUTTON, PauseProc},
748     {">", ForwardProc},
749     {">>", ToEndProc},
750     {NULL, NULL}
751 };
752
753 #define PIECE_MENU_SIZE 18
754 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
755     { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
756       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
757       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
758       N_("Empty square"), N_("Clear board") },
759     { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
760       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
761       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
762       N_("Empty square"), N_("Clear board") }
763 };
764 /* must be in same order as PieceMenuStrings! */
765 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
766     { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
767         WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
768         WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
769         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
770     { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
771         BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
772         BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
773         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
774 };
775
776 #define DROP_MENU_SIZE 6
777 String dropMenuStrings[DROP_MENU_SIZE] = {
778     "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
779   };
780 /* must be in same order as PieceMenuStrings! */
781 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
782     (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
783     WhiteRook, WhiteQueen
784 };
785
786 typedef struct {
787     char piece;
788     char* widget;
789 } DropMenuEnables;
790
791 DropMenuEnables dmEnables[] = {
792     { 'P', "Pawn" },
793     { 'N', "Knight" },
794     { 'B', "Bishop" },
795     { 'R', "Rook" },
796     { 'Q', "Queen" }
797 };
798
799 Arg shellArgs[] = {
800     { XtNwidth, 0 },
801     { XtNheight, 0 },
802     { XtNminWidth, 0 },
803     { XtNminHeight, 0 },
804     { XtNmaxWidth, 0 },
805     { XtNmaxHeight, 0 }
806 };
807
808 Arg layoutArgs[] = {
809     { XtNborderWidth, 0 },
810     { XtNdefaultDistance, 0 },
811 };
812
813 Arg formArgs[] = {
814     { XtNborderWidth, 0 },
815     { XtNresizable, (XtArgVal) True },
816 };
817
818 Arg boardArgs[] = {
819     { XtNborderWidth, 0 },
820     { XtNwidth, 0 },
821     { XtNheight, 0 }
822 };
823
824 Arg titleArgs[] = {
825     { XtNjustify, (XtArgVal) XtJustifyRight },
826     { XtNlabel, (XtArgVal) "..." },
827     { XtNresizable, (XtArgVal) True },
828     { XtNresize, (XtArgVal) False }
829 };
830
831 Arg messageArgs[] = {
832     { XtNjustify, (XtArgVal) XtJustifyLeft },
833     { XtNlabel, (XtArgVal) "..." },
834     { XtNresizable, (XtArgVal) True },
835     { XtNresize, (XtArgVal) False }
836 };
837
838 Arg timerArgs[] = {
839     { XtNborderWidth, 0 },
840     { XtNjustify, (XtArgVal) XtJustifyLeft }
841 };
842
843 XtResource clientResources[] = {
844     { "flashCount", "flashCount", XtRInt, sizeof(int),
845         XtOffset(AppDataPtr, flashCount), XtRImmediate,
846         (XtPointer) FLASH_COUNT  },
847 };
848
849 XrmOptionDescRec shellOptions[] = {
850     { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
851     { "-flash", "flashCount", XrmoptionNoArg, "3" },
852     { "-xflash", "flashCount", XrmoptionNoArg, "0" },
853 };
854
855 XtActionsRec boardActions[] = {
856     { "DrawPosition", DrawPositionProc },
857     { "HandleUserMove", HandleUserMove },
858     { "AnimateUserMove", AnimateUserMove },
859     { "HandlePV", HandlePV },
860     { "SelectPV", SelectPV },
861     { "StopPV", StopPV },
862     { "FileNameAction", FileNameAction },
863     { "AskQuestionProc", AskQuestionProc },
864     { "AskQuestionReplyAction", AskQuestionReplyAction },
865     { "PieceMenuPopup", PieceMenuPopup },
866     { "WhiteClock", WhiteClock },
867     { "BlackClock", BlackClock },
868     { "Iconify", Iconify },
869     { "ResetProc", ResetProc },
870     { "NewVariantProc", NewVariantProc },
871     { "LoadGameProc", LoadGameProc },
872     { "LoadNextGameProc", LoadNextGameProc },
873     { "LoadPrevGameProc", LoadPrevGameProc },
874     { "LoadSelectedProc", LoadSelectedProc },
875     { "SetFilterProc", SetFilterProc },
876     { "ReloadGameProc", ReloadGameProc },
877     { "LoadPositionProc", LoadPositionProc },
878     { "LoadNextPositionProc", LoadNextPositionProc },
879     { "LoadPrevPositionProc", LoadPrevPositionProc },
880     { "ReloadPositionProc", ReloadPositionProc },
881     { "CopyPositionProc", CopyPositionProc },
882     { "PastePositionProc", PastePositionProc },
883     { "CopyGameProc", CopyGameProc },
884     { "PasteGameProc", PasteGameProc },
885     { "SaveGameProc", SaveGameProc },
886     { "SavePositionProc", SavePositionProc },
887     { "MailMoveProc", MailMoveProc },
888     { "ReloadCmailMsgProc", ReloadCmailMsgProc },
889     { "QuitProc", QuitProc },
890     { "MachineWhiteProc", MachineWhiteProc },
891     { "MachineBlackProc", MachineBlackProc },
892     { "AnalysisModeProc", AnalyzeModeProc },
893     { "AnalyzeFileProc", AnalyzeFileProc },
894     { "TwoMachinesProc", TwoMachinesProc },
895     { "IcsClientProc", IcsClientProc },
896     { "EditGameProc", EditGameProc },
897     { "EditPositionProc", EditPositionProc },
898     { "TrainingProc", EditPositionProc },
899     { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
900     { "EvalGraphProc", EvalGraphProc},       // [HGM] Winboard_x avaluation graph window
901     { "ShowGameListProc", ShowGameListProc },
902     { "ShowMoveListProc", HistoryShowProc},
903     { "EditTagsProc", EditCommentProc },
904     { "EditCommentProc", EditCommentProc },
905     { "IcsAlarmProc", IcsAlarmProc },
906     { "IcsInputBoxProc", IcsInputBoxProc },
907     { "PauseProc", PauseProc },
908     { "AcceptProc", AcceptProc },
909     { "DeclineProc", DeclineProc },
910     { "RematchProc", RematchProc },
911     { "CallFlagProc", CallFlagProc },
912     { "DrawProc", DrawProc },
913     { "AdjournProc", AdjournProc },
914     { "AbortProc", AbortProc },
915     { "ResignProc", ResignProc },
916     { "AdjuWhiteProc", AdjuWhiteProc },
917     { "AdjuBlackProc", AdjuBlackProc },
918     { "AdjuDrawProc", AdjuDrawProc },
919     { "EnterKeyProc", EnterKeyProc },
920     { "UpKeyProc", UpKeyProc },
921     { "DownKeyProc", DownKeyProc },
922     { "StopObservingProc", StopObservingProc },
923     { "StopExaminingProc", StopExaminingProc },
924     { "UploadProc", UploadProc },
925     { "BackwardProc", BackwardProc },
926     { "ForwardProc", ForwardProc },
927     { "ToStartProc", ToStartProc },
928     { "ToEndProc", ToEndProc },
929     { "RevertProc", RevertProc },
930     { "AnnotateProc", AnnotateProc },
931     { "TruncateGameProc", TruncateGameProc },
932     { "MoveNowProc", MoveNowProc },
933     { "RetractMoveProc", RetractMoveProc },
934     { "EngineMenuProc", (XtActionProc) EngineMenuProc },
935     { "UciMenuProc", (XtActionProc) UciMenuProc },
936     { "TimeControlProc", (XtActionProc) TimeControlProc },
937     { "AlwaysQueenProc", AlwaysQueenProc },
938     { "AnimateDraggingProc", AnimateDraggingProc },
939     { "AnimateMovingProc", AnimateMovingProc },
940     { "AutoflagProc", AutoflagProc },
941     { "AutoflipProc", AutoflipProc },
942     { "AutobsProc", AutobsProc },
943     { "AutoraiseProc", AutoraiseProc },
944     { "AutosaveProc", AutosaveProc },
945     { "BlindfoldProc", BlindfoldProc },
946     { "FlashMovesProc", FlashMovesProc },
947     { "FlipViewProc", FlipViewProc },
948     { "GetMoveListProc", GetMoveListProc },
949 #if HIGHDRAG
950     { "HighlightDraggingProc", HighlightDraggingProc },
951 #endif
952     { "HighlightLastMoveProc", HighlightLastMoveProc },
953     { "IcsAlarmProc", IcsAlarmProc },
954     { "MoveSoundProc", MoveSoundProc },
955     { "OldSaveStyleProc", OldSaveStyleProc },
956     { "PeriodicUpdatesProc", PeriodicUpdatesProc },
957     { "PonderNextMoveProc", PonderNextMoveProc },
958     { "PopupExitMessageProc", PopupExitMessageProc },
959     { "PopupMoveErrorsProc", PopupMoveErrorsProc },
960     { "PremoveProc", PremoveProc },
961     { "QuietPlayProc", QuietPlayProc },
962     { "ShowCoordsProc", ShowCoordsProc },
963     { "ShowThinkingProc", ShowThinkingProc },
964     { "HideThinkingProc", HideThinkingProc },
965     { "TestLegalityProc", TestLegalityProc },
966     { "SaveSettingsProc", SaveSettingsProc },
967     { "SaveOnExitProc", SaveOnExitProc },
968     { "InfoProc", InfoProc },
969     { "ManProc", ManProc },
970     { "HintProc", HintProc },
971     { "BookProc", BookProc },
972     { "AboutGameProc", AboutGameProc },
973     { "AboutProc", AboutProc },
974     { "DebugProc", DebugProc },
975     { "NothingProc", NothingProc },
976     { "CommentClick", (XtActionProc) CommentClick },
977     { "CommentPopDown", (XtActionProc) CommentPopDown },
978     { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
979     { "TagsPopDown", (XtActionProc) TagsPopDown },
980     { "ErrorPopDown", (XtActionProc) ErrorPopDown },
981     { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
982     { "FileNamePopDown", (XtActionProc) FileNamePopDown },
983     { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
984     { "GameListPopDown", (XtActionProc) GameListPopDown },
985     { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
986     { "PromotionPopDown", (XtActionProc) PromotionPopDown },
987     { "HistoryPopDown", (XtActionProc) HistoryPopDown },
988     { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
989     { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
990     { "ShufflePopDown", (XtActionProc) ShufflePopDown },
991     { "EnginePopDown", (XtActionProc) EnginePopDown },
992     { "UciPopDown", (XtActionProc) UciPopDown },
993     { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
994     { "NewVariantPopDown", (XtActionProc) NewVariantPopDown },
995     { "SettingsPopDown", (XtActionProc) SettingsPopDown },
996     { "CopyMemoProc", (XtActionProc) CopyMemoProc },
997 };
998
999 char globalTranslations[] =
1000   ":<Key>F9: ResignProc() \n \
1001    :Ctrl<Key>n: ResetProc() \n \
1002    :Meta<Key>V: NewVariantProc() \n \
1003    :Ctrl<Key>o: LoadGameProc() \n \
1004    :Meta<Key>Next: LoadNextGameProc() \n \
1005    :Meta<Key>Prior: LoadPrevGameProc() \n \
1006    :Ctrl<Key>s: SaveGameProc() \n \
1007    :Ctrl<Key>c: CopyGameProc() \n \
1008    :Ctrl<Key>v: PasteGameProc() \n \
1009    :Ctrl<Key>O: LoadPositionProc() \n \
1010    :Shift Meta<Key>Next: LoadNextPositionProc() \n \
1011    :Shift Meta<Key>Prior: LoadPrevPositionProc() \n \
1012    :Ctrl<Key>S: SavePositionProc() \n \
1013    :Ctrl<Key>C: CopyPositionProc() \n \
1014    :Ctrl<Key>V: PastePositionProc() \n \
1015    :Ctrl<Key>q: QuitProc() \n \
1016    :Ctrl<Key>w: MachineWhiteProc() \n \
1017    :Ctrl<Key>b: MachineBlackProc() \n \
1018    :Ctrl<Key>t: TwoMachinesProc() \n \
1019    :Ctrl<Key>a: AnalysisModeProc() \n \
1020    :Ctrl<Key>f: AnalyzeFileProc() \n \
1021    :Ctrl<Key>e: EditGameProc() \n \
1022    :Ctrl<Key>E: EditPositionProc() \n \
1023    :Meta<Key>O: EngineOutputProc() \n \
1024    :Meta<Key>E: EvalGraphProc() \n \
1025    :Meta<Key>G: ShowGameListProc() \n \
1026    :Meta<Key>H: ShowMoveListProc() \n \
1027    :<Key>Pause: PauseProc() \n \
1028    :<Key>F3: AcceptProc() \n \
1029    :<Key>F4: DeclineProc() \n \
1030    :<Key>F12: RematchProc() \n \
1031    :<Key>F5: CallFlagProc() \n \
1032    :<Key>F6: DrawProc() \n \
1033    :<Key>F7: AdjournProc() \n \
1034    :<Key>F8: AbortProc() \n \
1035    :<Key>F10: StopObservingProc() \n \
1036    :<Key>F11: StopExaminingProc() \n \
1037    :Meta Ctrl<Key>F12: DebugProc() \n \
1038    :Meta<Key>End: ToEndProc() \n \
1039    :Meta<Key>Right: ForwardProc() \n \
1040    :Meta<Key>Home: ToStartProc() \n \
1041    :Meta<Key>Left: BackwardProc() \n \
1042    :Ctrl<Key>m: MoveNowProc() \n \
1043    :Ctrl<Key>x: RetractMoveProc() \n \
1044    :Meta<Key>J: EngineMenuProc() \n \
1045    :Meta<Key>U: UciMenuProc() \n \
1046    :Meta<Key>T: TimeControlProc() \n \
1047    :Ctrl<Key>Q: AlwaysQueenProc() \n \
1048    :Ctrl<Key>F: AutoflagProc() \n \
1049    :Ctrl<Key>A: AnimateMovingProc() \n \
1050    :Ctrl<Key>P: PonderNextMoveProc() \n \
1051    :Ctrl<Key>L: TestLegalityProc() \n \
1052    :Ctrl<Key>H: HideThinkingProc() \n \
1053    :<Key>-: Iconify() \n \
1054    :<Key>F1: ManProc() \n \
1055    :<Key>F2: FlipViewProc() \n \
1056    <KeyDown>.: BackwardProc() \n \
1057    <KeyUp>.: ForwardProc() \n \
1058    Shift<Key>1: AskQuestionProc(\"Direct command\",\
1059                                 \"Send to chess program:\",,1) \n \
1060    Shift<Key>2: AskQuestionProc(\"Direct command\",\
1061                                 \"Send to second chess program:\",,2) \n";
1062
1063 char boardTranslations[] =
1064    "<Btn1Down>: HandleUserMove() \n \
1065    <Btn1Up>: HandleUserMove() \n \
1066    <Btn1Motion>: AnimateUserMove() \n \
1067    <Btn3Motion>: HandlePV() \n \
1068    <Btn3Up>: PieceMenuPopup(menuB) \n \
1069    Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1070                  PieceMenuPopup(menuB) \n \
1071    Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1072                  PieceMenuPopup(menuW) \n \
1073    Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1074                  PieceMenuPopup(menuW) \n \
1075    Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1076                  PieceMenuPopup(menuB) \n";
1077
1078 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1079 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1080
1081 char ICSInputTranslations[] =
1082     "<Key>Up: UpKeyProc() \n "
1083     "<Key>Down: DownKeyProc() \n "
1084     "<Key>Return: EnterKeyProc() \n";
1085
1086 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1087 //             as the widget is destroyed before the up-click can call extend-end
1088 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1089
1090 String xboardResources[] = {
1091     "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1092     "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1093     "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1094     NULL
1095   };
1096
1097
1098 /* Max possible square size */
1099 #define MAXSQSIZE 256
1100
1101 static int xpm_avail[MAXSQSIZE];
1102
1103 #ifdef HAVE_DIR_STRUCT
1104
1105 /* Extract piece size from filename */
1106 static int
1107 xpm_getsize(name, len, ext)
1108      char *name;
1109      int len;
1110      char *ext;
1111 {
1112     char *p, *d;
1113     char buf[10];
1114
1115     if (len < 4)
1116       return 0;
1117
1118     if ((p=strchr(name, '.')) == NULL ||
1119         StrCaseCmp(p+1, ext) != 0)
1120       return 0;
1121
1122     p = name + 3;
1123     d = buf;
1124
1125     while (*p && isdigit(*p))
1126       *(d++) = *(p++);
1127
1128     *d = 0;
1129     return atoi(buf);
1130 }
1131
1132 /* Setup xpm_avail */
1133 static int
1134 xpm_getavail(dirname, ext)
1135      char *dirname;
1136      char *ext;
1137 {
1138     DIR *dir;
1139     struct dirent *ent;
1140     int  i;
1141
1142     for (i=0; i<MAXSQSIZE; ++i)
1143       xpm_avail[i] = 0;
1144
1145     if (appData.debugMode)
1146       fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1147
1148     dir = opendir(dirname);
1149     if (!dir)
1150       {
1151           fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1152                   programName, dirname);
1153           exit(1);
1154       }
1155
1156     while ((ent=readdir(dir)) != NULL) {
1157         i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1158         if (i > 0 && i < MAXSQSIZE)
1159           xpm_avail[i] = 1;
1160     }
1161
1162     closedir(dir);
1163
1164     return 0;
1165 }
1166
1167 void
1168 xpm_print_avail(fp, ext)
1169      FILE *fp;
1170      char *ext;
1171 {
1172     int i;
1173
1174     fprintf(fp, _("Available `%s' sizes:\n"), ext);
1175     for (i=1; i<MAXSQSIZE; ++i) {
1176         if (xpm_avail[i])
1177           printf("%d\n", i);
1178     }
1179 }
1180
1181 /* Return XPM piecesize closest to size */
1182 int
1183 xpm_closest_to(dirname, size, ext)
1184      char *dirname;
1185      int size;
1186      char *ext;
1187 {
1188     int i;
1189     int sm_diff = MAXSQSIZE;
1190     int sm_index = 0;
1191     int diff;
1192
1193     xpm_getavail(dirname, ext);
1194
1195     if (appData.debugMode)
1196       xpm_print_avail(stderr, ext);
1197
1198     for (i=1; i<MAXSQSIZE; ++i) {
1199         if (xpm_avail[i]) {
1200             diff = size - i;
1201             diff = (diff<0) ? -diff : diff;
1202             if (diff < sm_diff) {
1203                 sm_diff = diff;
1204                 sm_index = i;
1205             }
1206         }
1207     }
1208
1209     if (!sm_index) {
1210         fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1211         exit(1);
1212     }
1213
1214     return sm_index;
1215 }
1216 #else   /* !HAVE_DIR_STRUCT */
1217 /* If we are on a system without a DIR struct, we can't
1218    read the directory, so we can't collect a list of
1219    filenames, etc., so we can't do any size-fitting. */
1220 int
1221 xpm_closest_to(dirname, size, ext)
1222      char *dirname;
1223      int size;
1224      char *ext;
1225 {
1226     fprintf(stderr, _("\
1227 Warning: No DIR structure found on this system --\n\
1228          Unable to autosize for XPM/XIM pieces.\n\
1229    Please report this error to frankm@hiwaay.net.\n\
1230    Include system type & operating system in message.\n"));
1231     return size;
1232 }
1233 #endif /* HAVE_DIR_STRUCT */
1234
1235 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1236                              "magenta", "cyan", "white" };
1237 typedef struct {
1238     int attr, bg, fg;
1239 } TextColors;
1240 TextColors textColors[(int)NColorClasses];
1241
1242 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1243 static int
1244 parse_color(str, which)
1245      char *str;
1246      int which;
1247 {
1248     char *p, buf[100], *d;
1249     int i;
1250
1251     if (strlen(str) > 99)       /* watch bounds on buf */
1252       return -1;
1253
1254     p = str;
1255     d = buf;
1256     for (i=0; i<which; ++i) {
1257         p = strchr(p, ',');
1258         if (!p)
1259           return -1;
1260         ++p;
1261     }
1262
1263     /* Could be looking at something like:
1264        black, , 1
1265        .. in which case we want to stop on a comma also */
1266     while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1267       ++p;
1268
1269     if (*p == ',') {
1270         return -1;              /* Use default for empty field */
1271     }
1272
1273     if (which == 2 || isdigit(*p))
1274       return atoi(p);
1275
1276     while (*p && isalpha(*p))
1277       *(d++) = *(p++);
1278
1279     *d = 0;
1280
1281     for (i=0; i<8; ++i) {
1282         if (!StrCaseCmp(buf, cnames[i]))
1283           return which? (i+40) : (i+30);
1284     }
1285     if (!StrCaseCmp(buf, "default")) return -1;
1286
1287     fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1288     return -2;
1289 }
1290
1291 static int
1292 parse_cpair(cc, str)
1293      ColorClass cc;
1294      char *str;
1295 {
1296     if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1297         fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1298                 programName, str);
1299         return -1;
1300     }
1301
1302     /* bg and attr are optional */
1303     textColors[(int)cc].bg = parse_color(str, 1);
1304     if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1305         textColors[(int)cc].attr = 0;
1306     }
1307     return 0;
1308 }
1309
1310
1311 /* Arrange to catch delete-window events */
1312 Atom wm_delete_window;
1313 void
1314 CatchDeleteWindow(Widget w, String procname)
1315 {
1316   char buf[MSG_SIZ];
1317   XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1318   snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1319   XtAugmentTranslations(w, XtParseTranslationTable(buf));
1320 }
1321
1322 void
1323 BoardToTop()
1324 {
1325   Arg args[16];
1326   XtSetArg(args[0], XtNiconic, False);
1327   XtSetValues(shellWidget, args, 1);
1328
1329   XtPopup(shellWidget, XtGrabNone); /* Raise if lowered  */
1330 }
1331
1332 //---------------------------------------------------------------------------------------------------------
1333 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1334 #define XBOARD True
1335 #define JAWS_ARGS
1336 #define CW_USEDEFAULT (1<<31)
1337 #define ICS_TEXT_MENU_SIZE 90
1338 #define DEBUG_FILE "xboard.debug"
1339 #define SetCurrentDirectory chdir
1340 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1341 #define OPTCHAR "-"
1342 #define SEPCHAR " "
1343
1344 // these two must some day move to frontend.h, when they are implemented
1345 Boolean GameListIsUp();
1346
1347 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1348 #include "args.h"
1349
1350 // front-end part of option handling
1351
1352 // [HGM] This platform-dependent table provides the location for storing the color info
1353 extern char *crWhite, * crBlack;
1354
1355 void *
1356 colorVariable[] = {
1357   &appData.whitePieceColor,
1358   &appData.blackPieceColor,
1359   &appData.lightSquareColor,
1360   &appData.darkSquareColor,
1361   &appData.highlightSquareColor,
1362   &appData.premoveHighlightColor,
1363   &appData.lowTimeWarningColor,
1364   NULL,
1365   NULL,
1366   NULL,
1367   NULL,
1368   NULL,
1369   &crWhite,
1370   &crBlack,
1371   NULL
1372 };
1373
1374 // [HGM] font: keep a font for each square size, even non-stndard ones
1375 #define NUM_SIZES 18
1376 #define MAX_SIZE 130
1377 Boolean fontSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1378 char *fontTable[NUM_FONTS][MAX_SIZE];
1379
1380 void
1381 ParseFont(char *name, int number)
1382 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1383   int size;
1384   if(sscanf(name, "size%d:", &size)) {
1385     // [HGM] font: font is meant for specific boardSize (likely from settings file);
1386     //       defer processing it until we know if it matches our board size
1387     if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1388         fontTable[number][size] = strdup(strchr(name, ':')+1);
1389         fontValid[number][size] = True;
1390     }
1391     return;
1392   }
1393   switch(number) {
1394     case 0: // CLOCK_FONT
1395         appData.clockFont = strdup(name);
1396       break;
1397     case 1: // MESSAGE_FONT
1398         appData.font = strdup(name);
1399       break;
1400     case 2: // COORD_FONT
1401         appData.coordFont = strdup(name);
1402       break;
1403     default:
1404       return;
1405   }
1406   fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1407 }
1408
1409 void
1410 SetFontDefaults()
1411 { // only 2 fonts currently
1412   appData.clockFont = CLOCK_FONT_NAME;
1413   appData.coordFont = COORD_FONT_NAME;
1414   appData.font  =   DEFAULT_FONT_NAME;
1415 }
1416
1417 void
1418 CreateFonts()
1419 { // no-op, until we identify the code for this already in XBoard and move it here
1420 }
1421
1422 void
1423 ParseColor(int n, char *name)
1424 { // in XBoard, just copy the color-name string
1425   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1426 }
1427
1428 void
1429 ParseTextAttribs(ColorClass cc, char *s)
1430 {
1431     (&appData.colorShout)[cc] = strdup(s);
1432 }
1433
1434 void
1435 ParseBoardSize(void *addr, char *name)
1436 {
1437     appData.boardSize = strdup(name);
1438 }
1439
1440 void
1441 LoadAllSounds()
1442 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1443 }
1444
1445 void
1446 SetCommPortDefaults()
1447 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1448 }
1449
1450 // [HGM] args: these three cases taken out to stay in front-end
1451 void
1452 SaveFontArg(FILE *f, ArgDescriptor *ad)
1453 {
1454   char *name;
1455   int i, n = (int)ad->argLoc;
1456   switch(n) {
1457     case 0: // CLOCK_FONT
1458         name = appData.clockFont;
1459       break;
1460     case 1: // MESSAGE_FONT
1461         name = appData.font;
1462       break;
1463     case 2: // COORD_FONT
1464         name = appData.coordFont;
1465       break;
1466     default:
1467       return;
1468   }
1469   for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1470     if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1471         fontTable[n][squareSize] = strdup(name);
1472         fontValid[n][squareSize] = True;
1473         break;
1474   }
1475   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1476     fprintf(f, OPTCHAR "%s" SEPCHAR "size%d:%s\n", ad->argName, i, fontTable[n][i]);
1477 }
1478
1479 void
1480 ExportSounds()
1481 { // nothing to do, as the sounds are at all times represented by their text-string names already
1482 }
1483
1484 void
1485 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1486 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1487         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
1488 }
1489
1490 void
1491 SaveColor(FILE *f, ArgDescriptor *ad)
1492 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1493         if(colorVariable[(int)ad->argLoc])
1494         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
1495 }
1496
1497 void
1498 SaveBoardSize(FILE *f, char *name, void *addr)
1499 { // wrapper to shield back-end from BoardSize & sizeInfo
1500   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1501 }
1502
1503 void
1504 ParseCommPortSettings(char *s)
1505 { // no such option in XBoard (yet)
1506 }
1507
1508 extern Widget engineOutputShell;
1509 extern Widget tagsShell, editTagsShell;
1510 void
1511 GetActualPlacement(Widget wg, WindowPlacement *wp)
1512 {
1513   Arg args[16];
1514   Dimension w, h;
1515   Position x, y;
1516   int i;
1517
1518   if(!wg) return;
1519
1520     i = 0;
1521     XtSetArg(args[i], XtNx, &x); i++;
1522     XtSetArg(args[i], XtNy, &y); i++;
1523     XtSetArg(args[i], XtNwidth, &w); i++;
1524     XtSetArg(args[i], XtNheight, &h); i++;
1525     XtGetValues(wg, args, i);
1526     wp->x = x - 4;
1527     wp->y = y - 23;
1528     wp->height = h;
1529     wp->width = w;
1530 }
1531
1532 void
1533 GetWindowCoords()
1534 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1535   // In XBoard this will have to wait until awareness of window parameters is implemented
1536   GetActualPlacement(shellWidget, &wpMain);
1537   if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1538   if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1539   if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1540   if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1541   if(commentShell) GetActualPlacement(commentShell, &wpComment);
1542   else             GetActualPlacement(editShell,    &wpComment);
1543   if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1544   else      GetActualPlacement(editTagsShell, &wpTags);
1545 }
1546
1547 void
1548 PrintCommPortSettings(FILE *f, char *name)
1549 { // This option does not exist in XBoard
1550 }
1551
1552 int
1553 MySearchPath(char *installDir, char *name, char *fullname)
1554 { // just append installDir and name. Perhaps ExpandPath should be used here?
1555   name = ExpandPathName(name);
1556   if(name && name[0] == '/')
1557     safeStrCpy(fullname, name, MSG_SIZ );
1558   else {
1559     sprintf(fullname, "%s%c%s", installDir, '/', name);
1560   }
1561   return 1;
1562 }
1563
1564 int
1565 MyGetFullPathName(char *name, char *fullname)
1566 { // should use ExpandPath?
1567   name = ExpandPathName(name);
1568   safeStrCpy(fullname, name, MSG_SIZ );
1569   return 1;
1570 }
1571
1572 void
1573 EnsureOnScreen(int *x, int *y, int minX, int minY)
1574 {
1575   return;
1576 }
1577
1578 int
1579 MainWindowUp()
1580 { // [HGM] args: allows testing if main window is realized from back-end
1581   return xBoardWindow != 0;
1582 }
1583
1584 void
1585 PopUpStartupDialog()
1586 {  // start menu not implemented in XBoard
1587 }
1588
1589 char *
1590 ConvertToLine(int argc, char **argv)
1591 {
1592   static char line[128*1024], buf[1024];
1593   int i;
1594
1595   line[0] = NULLCHAR;
1596   for(i=1; i<argc; i++)
1597     {
1598       if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1599           && argv[i][0] != '{' )
1600         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1601       else
1602         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1603       strncat(line, buf, 128*1024 - strlen(line) - 1 );
1604     }
1605
1606   line[strlen(line)-1] = NULLCHAR;
1607   return line;
1608 }
1609
1610 //--------------------------------------------------------------------------------------------
1611
1612 extern Boolean twoBoards, partnerUp;
1613
1614 #ifdef IDSIZES
1615   // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1616 #else
1617 #define BoardSize int
1618 void InitDrawingSizes(BoardSize boardSize, int flags)
1619 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1620     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1621     Arg args[16];
1622     XtGeometryResult gres;
1623     int i;
1624
1625     if(!formWidget) return;
1626
1627     /*
1628      * Enable shell resizing.
1629      */
1630     shellArgs[0].value = (XtArgVal) &w;
1631     shellArgs[1].value = (XtArgVal) &h;
1632     XtGetValues(shellWidget, shellArgs, 2);
1633
1634     shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1635     shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1636     XtSetValues(shellWidget, &shellArgs[2], 4);
1637
1638     XtSetArg(args[0], XtNdefaultDistance, &sep);
1639     XtGetValues(formWidget, args, 1);
1640
1641     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1642     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1643     CreateGrid();
1644     hOffset = boardWidth + 10;
1645     for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1646         secondSegments[i] = gridSegments[i];
1647         secondSegments[i].x1 += hOffset;
1648         secondSegments[i].x2 += hOffset;
1649     }
1650
1651     XtSetArg(args[0], XtNwidth, boardWidth);
1652     XtSetArg(args[1], XtNheight, boardHeight);
1653     XtSetValues(boardWidget, args, 2);
1654
1655     timerWidth = (boardWidth - sep) / 2;
1656     XtSetArg(args[0], XtNwidth, timerWidth);
1657     XtSetValues(whiteTimerWidget, args, 1);
1658     XtSetValues(blackTimerWidget, args, 1);
1659
1660     XawFormDoLayout(formWidget, False);
1661
1662     if (appData.titleInWindow) {
1663         i = 0;
1664         XtSetArg(args[i], XtNborderWidth, &bor); i++;
1665         XtSetArg(args[i], XtNheight, &h);  i++;
1666         XtGetValues(titleWidget, args, i);
1667         if (smallLayout) {
1668             w = boardWidth - 2*bor;
1669         } else {
1670             XtSetArg(args[0], XtNwidth, &w);
1671             XtGetValues(menuBarWidget, args, 1);
1672             w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1673         }
1674
1675         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1676         if (gres != XtGeometryYes && appData.debugMode) {
1677             fprintf(stderr,
1678                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1679                     programName, gres, w, h, wr, hr);
1680         }
1681     }
1682
1683     XawFormDoLayout(formWidget, True);
1684
1685     /*
1686      * Inhibit shell resizing.
1687      */
1688     shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1689     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1690     shellArgs[4].value = shellArgs[2].value = w;
1691     shellArgs[5].value = shellArgs[3].value = h;
1692     XtSetValues(shellWidget, &shellArgs[0], 6);
1693
1694     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1695     // (only for xpm)
1696     if(useImages) {
1697       for(i=0; i<4; i++) {
1698         int p;
1699         for(p=0; p<=(int)WhiteKing; p++)
1700            xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1701         if(gameInfo.variant == VariantShogi) {
1702            xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1703            xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1704            xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1705            xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1706            xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1707         }
1708 #ifdef GOTHIC
1709         if(gameInfo.variant == VariantGothic) {
1710            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1711         }
1712 #endif
1713 #if !HAVE_LIBXPM
1714         // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1715         for(p=0; p<=(int)WhiteKing; p++)
1716            ximMaskPm[p] = ximMaskPm2[p]; // defaults
1717         if(gameInfo.variant == VariantShogi) {
1718            ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1719            ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1720            ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1721            ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1722            ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1723         }
1724 #ifdef GOTHIC
1725         if(gameInfo.variant == VariantGothic) {
1726            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1727         }
1728 #endif
1729 #endif
1730       }
1731     } else {
1732       for(i=0; i<2; i++) {
1733         int p;
1734         for(p=0; p<=(int)WhiteKing; p++)
1735            pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1736         if(gameInfo.variant == VariantShogi) {
1737            pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1738            pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1739            pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1740            pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1741            pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1742         }
1743 #ifdef GOTHIC
1744         if(gameInfo.variant == VariantGothic) {
1745            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1746         }
1747 #endif
1748       }
1749     }
1750 #if HAVE_LIBXPM
1751     CreateAnimVars();
1752 #endif
1753 }
1754 #endif
1755
1756 int
1757 main(argc, argv)
1758      int argc;
1759      char **argv;
1760 {
1761     int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1762     XSetWindowAttributes window_attributes;
1763     Arg args[16];
1764     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1765     XrmValue vFrom, vTo;
1766     XtGeometryResult gres;
1767     char *p;
1768     XrmDatabase xdb;
1769     int forceMono = False;
1770
1771     srandom(time(0)); // [HGM] book: make random truly random
1772
1773     setbuf(stdout, NULL);
1774     setbuf(stderr, NULL);
1775     debugFP = stderr;
1776
1777     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1778         printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1779         exit(0);
1780     }
1781
1782     programName = strrchr(argv[0], '/');
1783     if (programName == NULL)
1784       programName = argv[0];
1785     else
1786       programName++;
1787
1788 #ifdef ENABLE_NLS
1789     XtSetLanguageProc(NULL, NULL, NULL);
1790     bindtextdomain(PACKAGE, LOCALEDIR);
1791     textdomain(PACKAGE);
1792 #endif
1793
1794     shellWidget =
1795       XtAppInitialize(&appContext, "XBoard", shellOptions,
1796                       XtNumber(shellOptions),
1797                       &argc, argv, xboardResources, NULL, 0);
1798     appData.boardSize = "";
1799     InitAppData(ConvertToLine(argc, argv));
1800     p = getenv("HOME");
1801     if (p == NULL) p = "/tmp";
1802     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1803     gameCopyFilename = (char*) malloc(i);
1804     gamePasteFilename = (char*) malloc(i);
1805     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1806     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1807
1808     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1809                               clientResources, XtNumber(clientResources),
1810                               NULL, 0);
1811
1812     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1813         static char buf[MSG_SIZ];
1814         EscapeExpand(buf, appData.initString);
1815         appData.initString = strdup(buf);
1816         EscapeExpand(buf, appData.secondInitString);
1817         appData.secondInitString = strdup(buf);
1818         EscapeExpand(buf, appData.firstComputerString);
1819         appData.firstComputerString = strdup(buf);
1820         EscapeExpand(buf, appData.secondComputerString);
1821         appData.secondComputerString = strdup(buf);
1822     }
1823
1824     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1825         chessDir = ".";
1826     } else {
1827         if (chdir(chessDir) != 0) {
1828             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1829             perror(chessDir);
1830             exit(1);
1831         }
1832     }
1833
1834     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1835         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1836         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
1837            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1838            exit(errno);
1839         }
1840         setbuf(debugFP, NULL);
1841     }
1842
1843     /* [HGM,HR] make sure board size is acceptable */
1844     if(appData.NrFiles > BOARD_FILES ||
1845        appData.NrRanks > BOARD_RANKS   )
1846          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1847
1848 #if !HIGHDRAG
1849     /* This feature does not work; animation needs a rewrite */
1850     appData.highlightDragging = FALSE;
1851 #endif
1852     InitBackEnd1();
1853
1854     xDisplay = XtDisplay(shellWidget);
1855     xScreen = DefaultScreen(xDisplay);
1856     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1857
1858         gameInfo.variant = StringToVariant(appData.variant);
1859         InitPosition(FALSE);
1860
1861 #ifdef IDSIZE
1862     InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1863 #else
1864     if (isdigit(appData.boardSize[0])) {
1865         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1866                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1867                    &fontPxlSize, &smallLayout, &tinyLayout);
1868         if (i == 0) {
1869             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1870                     programName, appData.boardSize);
1871             exit(2);
1872         }
1873         if (i < 7) {
1874             /* Find some defaults; use the nearest known size */
1875             SizeDefaults *szd, *nearest;
1876             int distance = 99999;
1877             nearest = szd = sizeDefaults;
1878             while (szd->name != NULL) {
1879                 if (abs(szd->squareSize - squareSize) < distance) {
1880                     nearest = szd;
1881                     distance = abs(szd->squareSize - squareSize);
1882                     if (distance == 0) break;
1883                 }
1884                 szd++;
1885             }
1886             if (i < 2) lineGap = nearest->lineGap;
1887             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1888             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1889             if (i < 5) fontPxlSize = nearest->fontPxlSize;
1890             if (i < 6) smallLayout = nearest->smallLayout;
1891             if (i < 7) tinyLayout = nearest->tinyLayout;
1892         }
1893     } else {
1894         SizeDefaults *szd = sizeDefaults;
1895         if (*appData.boardSize == NULLCHAR) {
1896             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1897                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1898               szd++;
1899             }
1900             if (szd->name == NULL) szd--;
1901             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1902         } else {
1903             while (szd->name != NULL &&
1904                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1905             if (szd->name == NULL) {
1906                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1907                         programName, appData.boardSize);
1908                 exit(2);
1909             }
1910         }
1911         squareSize = szd->squareSize;
1912         lineGap = szd->lineGap;
1913         clockFontPxlSize = szd->clockFontPxlSize;
1914         coordFontPxlSize = szd->coordFontPxlSize;
1915         fontPxlSize = szd->fontPxlSize;
1916         smallLayout = szd->smallLayout;
1917         tinyLayout = szd->tinyLayout;
1918         // [HGM] font: use defaults from settings file if available and not overruled
1919     }
1920     if(!fontSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1921         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1922     if(!fontSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1923         appData.font = fontTable[MESSAGE_FONT][squareSize];
1924     if(!fontSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1925         appData.coordFont = fontTable[COORD_FONT][squareSize];
1926
1927     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1928     if (strlen(appData.pixmapDirectory) > 0) {
1929         p = ExpandPathName(appData.pixmapDirectory);
1930         if (!p) {
1931             fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1932                    appData.pixmapDirectory);
1933             exit(1);
1934         }
1935         if (appData.debugMode) {
1936           fprintf(stderr, _("\
1937 XBoard square size (hint): %d\n\
1938 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1939         }
1940         squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1941         if (appData.debugMode) {
1942             fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1943         }
1944     }
1945
1946     /* [HR] height treated separately (hacked) */
1947     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1948     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1949     if (appData.showJail == 1) {
1950         /* Jail on top and bottom */
1951         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1952         XtSetArg(boardArgs[2], XtNheight,
1953                  boardHeight + 2*(lineGap + squareSize));
1954     } else if (appData.showJail == 2) {
1955         /* Jail on sides */
1956         XtSetArg(boardArgs[1], XtNwidth,
1957                  boardWidth + 2*(lineGap + squareSize));
1958         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1959     } else {
1960         /* No jail */
1961         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1962         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1963     }
1964
1965     /*
1966      * Determine what fonts to use.
1967      */
1968     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1969     clockFontID = XLoadFont(xDisplay, appData.clockFont);
1970     clockFontStruct = XQueryFont(xDisplay, clockFontID);
1971     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1972     coordFontID = XLoadFont(xDisplay, appData.coordFont);
1973     coordFontStruct = XQueryFont(xDisplay, coordFontID);
1974     appData.font = FindFont(appData.font, fontPxlSize);
1975     countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
1976     countFontStruct = XQueryFont(xDisplay, countFontID);
1977 //    appData.font = FindFont(appData.font, fontPxlSize);
1978
1979     xdb = XtDatabase(xDisplay);
1980     XrmPutStringResource(&xdb, "*font", appData.font);
1981
1982     /*
1983      * Detect if there are not enough colors available and adapt.
1984      */
1985     if (DefaultDepth(xDisplay, xScreen) <= 2) {
1986       appData.monoMode = True;
1987     }
1988
1989     if (!appData.monoMode) {
1990         vFrom.addr = (caddr_t) appData.lightSquareColor;
1991         vFrom.size = strlen(appData.lightSquareColor);
1992         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1993         if (vTo.addr == NULL) {
1994           appData.monoMode = True;
1995           forceMono = True;
1996         } else {
1997           lightSquareColor = *(Pixel *) vTo.addr;
1998         }
1999     }
2000     if (!appData.monoMode) {
2001         vFrom.addr = (caddr_t) appData.darkSquareColor;
2002         vFrom.size = strlen(appData.darkSquareColor);
2003         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2004         if (vTo.addr == NULL) {
2005           appData.monoMode = True;
2006           forceMono = True;
2007         } else {
2008           darkSquareColor = *(Pixel *) vTo.addr;
2009         }
2010     }
2011     if (!appData.monoMode) {
2012         vFrom.addr = (caddr_t) appData.whitePieceColor;
2013         vFrom.size = strlen(appData.whitePieceColor);
2014         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2015         if (vTo.addr == NULL) {
2016           appData.monoMode = True;
2017           forceMono = True;
2018         } else {
2019           whitePieceColor = *(Pixel *) vTo.addr;
2020         }
2021     }
2022     if (!appData.monoMode) {
2023         vFrom.addr = (caddr_t) appData.blackPieceColor;
2024         vFrom.size = strlen(appData.blackPieceColor);
2025         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2026         if (vTo.addr == NULL) {
2027           appData.monoMode = True;
2028           forceMono = True;
2029         } else {
2030           blackPieceColor = *(Pixel *) vTo.addr;
2031         }
2032     }
2033
2034     if (!appData.monoMode) {
2035         vFrom.addr = (caddr_t) appData.highlightSquareColor;
2036         vFrom.size = strlen(appData.highlightSquareColor);
2037         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2038         if (vTo.addr == NULL) {
2039           appData.monoMode = True;
2040           forceMono = True;
2041         } else {
2042           highlightSquareColor = *(Pixel *) vTo.addr;
2043         }
2044     }
2045
2046     if (!appData.monoMode) {
2047         vFrom.addr = (caddr_t) appData.premoveHighlightColor;
2048         vFrom.size = strlen(appData.premoveHighlightColor);
2049         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2050         if (vTo.addr == NULL) {
2051           appData.monoMode = True;
2052           forceMono = True;
2053         } else {
2054           premoveHighlightColor = *(Pixel *) vTo.addr;
2055         }
2056     }
2057
2058     if (forceMono) {
2059       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2060               programName);
2061
2062       if (appData.bitmapDirectory == NULL ||
2063               appData.bitmapDirectory[0] == NULLCHAR)
2064             appData.bitmapDirectory = DEF_BITMAP_DIR;
2065     }
2066
2067     if (appData.lowTimeWarning && !appData.monoMode) {
2068       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2069       vFrom.size = strlen(appData.lowTimeWarningColor);
2070       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2071       if (vTo.addr == NULL)
2072                 appData.monoMode = True;
2073       else
2074                 lowTimeWarningColor = *(Pixel *) vTo.addr;
2075     }
2076
2077     if (appData.monoMode && appData.debugMode) {
2078         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2079                 (unsigned long) XWhitePixel(xDisplay, xScreen),
2080                 (unsigned long) XBlackPixel(xDisplay, xScreen));
2081     }
2082
2083     if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
2084         parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
2085         parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
2086         parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
2087         parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
2088         parse_cpair(ColorTell, appData.colorTell) < 0 ||
2089         parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
2090         parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
2091         parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
2092         parse_cpair(ColorNormal, appData.colorNormal) < 0)
2093       {
2094           if (appData.colorize) {
2095               fprintf(stderr,
2096                       _("%s: can't parse color names; disabling colorization\n"),
2097                       programName);
2098           }
2099           appData.colorize = FALSE;
2100       }
2101     textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2102     textColors[ColorNone].attr = 0;
2103
2104     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2105
2106     /*
2107      * widget hierarchy
2108      */
2109     if (tinyLayout) {
2110         layoutName = "tinyLayout";
2111     } else if (smallLayout) {
2112         layoutName = "smallLayout";
2113     } else {
2114         layoutName = "normalLayout";
2115     }
2116     /* Outer layoutWidget is there only to provide a name for use in
2117        resources that depend on the layout style */
2118     layoutWidget =
2119       XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2120                             layoutArgs, XtNumber(layoutArgs));
2121     formWidget =
2122       XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2123                             formArgs, XtNumber(formArgs));
2124     XtSetArg(args[0], XtNdefaultDistance, &sep);
2125     XtGetValues(formWidget, args, 1);
2126
2127     j = 0;
2128     widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2129     XtSetArg(args[0], XtNtop,    XtChainTop);
2130     XtSetArg(args[1], XtNbottom, XtChainTop);
2131     XtSetArg(args[2], XtNright,  XtChainLeft);
2132     XtSetValues(menuBarWidget, args, 3);
2133
2134     widgetList[j++] = whiteTimerWidget =
2135       XtCreateWidget("whiteTime", labelWidgetClass,
2136                      formWidget, timerArgs, XtNumber(timerArgs));
2137     XtSetArg(args[0], XtNfont, clockFontStruct);
2138     XtSetArg(args[1], XtNtop,    XtChainTop);
2139     XtSetArg(args[2], XtNbottom, XtChainTop);
2140     XtSetValues(whiteTimerWidget, args, 3);
2141
2142     widgetList[j++] = blackTimerWidget =
2143       XtCreateWidget("blackTime", labelWidgetClass,
2144                      formWidget, timerArgs, XtNumber(timerArgs));
2145     XtSetArg(args[0], XtNfont, clockFontStruct);
2146     XtSetArg(args[1], XtNtop,    XtChainTop);
2147     XtSetArg(args[2], XtNbottom, XtChainTop);
2148     XtSetValues(blackTimerWidget, args, 3);
2149
2150     if (appData.titleInWindow) {
2151         widgetList[j++] = titleWidget =
2152           XtCreateWidget("title", labelWidgetClass, formWidget,
2153                          titleArgs, XtNumber(titleArgs));
2154         XtSetArg(args[0], XtNtop,    XtChainTop);
2155         XtSetArg(args[1], XtNbottom, XtChainTop);
2156         XtSetValues(titleWidget, args, 2);
2157     }
2158
2159     if (appData.showButtonBar) {
2160       widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2161       XtSetArg(args[0], XtNleft,  XtChainRight); // [HGM] glue to right window edge
2162       XtSetArg(args[1], XtNright, XtChainRight); //       for good run-time sizing
2163       XtSetArg(args[2], XtNtop,    XtChainTop);
2164       XtSetArg(args[3], XtNbottom, XtChainTop);
2165       XtSetValues(buttonBarWidget, args, 4);
2166     }
2167
2168     widgetList[j++] = messageWidget =
2169       XtCreateWidget("message", labelWidgetClass, formWidget,
2170                      messageArgs, XtNumber(messageArgs));
2171     XtSetArg(args[0], XtNtop,    XtChainTop);
2172     XtSetArg(args[1], XtNbottom, XtChainTop);
2173     XtSetValues(messageWidget, args, 2);
2174
2175     widgetList[j++] = boardWidget =
2176       XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2177                      XtNumber(boardArgs));
2178
2179     XtManageChildren(widgetList, j);
2180
2181     timerWidth = (boardWidth - sep) / 2;
2182     XtSetArg(args[0], XtNwidth, timerWidth);
2183     XtSetValues(whiteTimerWidget, args, 1);
2184     XtSetValues(blackTimerWidget, args, 1);
2185
2186     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2187     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2188     XtGetValues(whiteTimerWidget, args, 2);
2189
2190     if (appData.showButtonBar) {
2191       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2192       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2193       XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2194     }
2195
2196     /*
2197      * formWidget uses these constraints but they are stored
2198      * in the children.
2199      */
2200     i = 0;
2201     XtSetArg(args[i], XtNfromHoriz, 0); i++;
2202     XtSetValues(menuBarWidget, args, i);
2203     if (appData.titleInWindow) {
2204         if (smallLayout) {
2205             i = 0;
2206             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2207             XtSetValues(whiteTimerWidget, args, i);
2208             i = 0;
2209             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2210             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2211             XtSetValues(blackTimerWidget, args, i);
2212             i = 0;
2213             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2214             XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2215             XtSetValues(titleWidget, args, i);
2216             i = 0;
2217             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2218             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2219             XtSetValues(messageWidget, args, i);
2220             if (appData.showButtonBar) {
2221               i = 0;
2222               XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2223               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2224               XtSetValues(buttonBarWidget, args, i);
2225             }
2226         } else {
2227             i = 0;
2228             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2229             XtSetValues(whiteTimerWidget, args, i);
2230             i = 0;
2231             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2232             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2233             XtSetValues(blackTimerWidget, args, i);
2234             i = 0;
2235             XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2236             XtSetValues(titleWidget, args, i);
2237             i = 0;
2238             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2239             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2240             XtSetValues(messageWidget, args, i);
2241             if (appData.showButtonBar) {
2242               i = 0;
2243               XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2244               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2245               XtSetValues(buttonBarWidget, args, i);
2246             }
2247         }
2248     } else {
2249         i = 0;
2250         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2251         XtSetValues(whiteTimerWidget, args, i);
2252         i = 0;
2253         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2254         XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2255         XtSetValues(blackTimerWidget, args, i);
2256         i = 0;
2257         XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2258         XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2259         XtSetValues(messageWidget, args, i);
2260         if (appData.showButtonBar) {
2261           i = 0;
2262           XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2263           XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2264           XtSetValues(buttonBarWidget, args, i);
2265         }
2266     }
2267     i = 0;
2268     XtSetArg(args[0], XtNfromVert, messageWidget);
2269     XtSetArg(args[1], XtNtop,    XtChainTop);
2270     XtSetArg(args[2], XtNbottom, XtChainBottom);
2271     XtSetArg(args[3], XtNleft,   XtChainLeft);
2272     XtSetArg(args[4], XtNright,  XtChainRight);
2273     XtSetValues(boardWidget, args, 5);
2274
2275     XtRealizeWidget(shellWidget);
2276
2277     if(wpMain.x > 0) {
2278       XtSetArg(args[0], XtNx, wpMain.x);
2279       XtSetArg(args[1], XtNy, wpMain.y);
2280       XtSetValues(shellWidget, args, 2);
2281     }
2282
2283     /*
2284      * Correct the width of the message and title widgets.
2285      * It is not known why some systems need the extra fudge term.
2286      * The value "2" is probably larger than needed.
2287      */
2288     XawFormDoLayout(formWidget, False);
2289
2290 #define WIDTH_FUDGE 2
2291     i = 0;
2292     XtSetArg(args[i], XtNborderWidth, &bor);  i++;
2293     XtSetArg(args[i], XtNheight, &h);  i++;
2294     XtGetValues(messageWidget, args, i);
2295     if (appData.showButtonBar) {
2296       i = 0;
2297       XtSetArg(args[i], XtNwidth, &w);  i++;
2298       XtGetValues(buttonBarWidget, args, i);
2299       w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2300     } else {
2301       w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2302     }
2303
2304     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2305     if (gres != XtGeometryYes && appData.debugMode) {
2306       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2307               programName, gres, w, h, wr, hr);
2308     }
2309
2310     /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2311     /* The size used for the child widget in layout lags one resize behind
2312        its true size, so we resize a second time, 1 pixel smaller.  Yeech! */
2313     w--;
2314     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2315     if (gres != XtGeometryYes && appData.debugMode) {
2316       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2317               programName, gres, w, h, wr, hr);
2318     }
2319     /* !! end hack */
2320     XtSetArg(args[0], XtNleft,  XtChainLeft);  // [HGM] glue ends for good run-time sizing
2321     XtSetArg(args[1], XtNright, XtChainRight);
2322     XtSetValues(messageWidget, args, 2);
2323
2324     if (appData.titleInWindow) {
2325         i = 0;
2326         XtSetArg(args[i], XtNborderWidth, &bor); i++;
2327         XtSetArg(args[i], XtNheight, &h);  i++;
2328         XtGetValues(titleWidget, args, i);
2329         if (smallLayout) {
2330             w = boardWidth - 2*bor;
2331         } else {
2332             XtSetArg(args[0], XtNwidth, &w);
2333             XtGetValues(menuBarWidget, args, 1);
2334             w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2335         }
2336
2337         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2338         if (gres != XtGeometryYes && appData.debugMode) {
2339             fprintf(stderr,
2340                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2341                     programName, gres, w, h, wr, hr);
2342         }
2343     }
2344     XawFormDoLayout(formWidget, True);
2345
2346     xBoardWindow = XtWindow(boardWidget);
2347
2348     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2349     //       not need to go into InitDrawingSizes().
2350 #endif
2351
2352     /*
2353      * Create X checkmark bitmap and initialize option menu checks.
2354      */
2355     ReadBitmap(&xMarkPixmap, "checkmark.bm",
2356                checkmark_bits, checkmark_width, checkmark_height);
2357     XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2358     if (appData.alwaysPromoteToQueen) {
2359         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2360                     args, 1);
2361     }
2362     if (appData.animateDragging) {
2363         XtSetValues(XtNameToWidget(menuBarWidget,
2364                                    "menuOptions.Animate Dragging"),
2365                     args, 1);
2366     }
2367     if (appData.animate) {
2368         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2369                     args, 1);
2370     }
2371     if (appData.autoComment) {
2372         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
2373                     args, 1);
2374     }
2375     if (appData.autoCallFlag) {
2376         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2377                     args, 1);
2378     }
2379     if (appData.autoFlipView) {
2380         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2381                     args, 1);
2382     }
2383     if (appData.autoObserve) {
2384         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
2385                     args, 1);
2386     }
2387     if (appData.autoRaiseBoard) {
2388         XtSetValues(XtNameToWidget(menuBarWidget,
2389                                    "menuOptions.Auto Raise Board"), args, 1);
2390     }
2391     if (appData.autoSaveGames) {
2392         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2393                     args, 1);
2394     }
2395     if (appData.saveGameFile[0] != NULLCHAR) {
2396         /* Can't turn this off from menu */
2397         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2398                     args, 1);
2399         XtSetSensitive(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2400                        False);
2401
2402     }
2403     if (appData.blindfold) {
2404         XtSetValues(XtNameToWidget(menuBarWidget,
2405                                    "menuOptions.Blindfold"), args, 1);
2406     }
2407     if (appData.flashCount > 0) {
2408         XtSetValues(XtNameToWidget(menuBarWidget,
2409                                    "menuOptions.Flash Moves"),
2410                     args, 1);
2411     }
2412     if (appData.getMoveList) {
2413         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
2414                     args, 1);
2415     }
2416 #if HIGHDRAG
2417     if (appData.highlightDragging) {
2418         XtSetValues(XtNameToWidget(menuBarWidget,
2419                                    "menuOptions.Highlight Dragging"),
2420                     args, 1);
2421     }
2422 #endif
2423     if (appData.highlightLastMove) {
2424         XtSetValues(XtNameToWidget(menuBarWidget,
2425                                    "menuOptions.Highlight Last Move"),
2426                     args, 1);
2427     }
2428     if (appData.icsAlarm) {
2429         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2430                     args, 1);
2431     }
2432     if (appData.ringBellAfterMoves) {
2433         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2434                     args, 1);
2435     }
2436     if (appData.oldSaveStyle) {
2437         XtSetValues(XtNameToWidget(menuBarWidget,
2438                                    "menuOptions.Old Save Style"), args, 1);
2439     }
2440     if (appData.periodicUpdates) {
2441         XtSetValues(XtNameToWidget(menuBarWidget,
2442                                    "menuOptions.Periodic Updates"), args, 1);
2443     }
2444     if (appData.ponderNextMove) {
2445         XtSetValues(XtNameToWidget(menuBarWidget,
2446                                    "menuOptions.Ponder Next Move"), args, 1);
2447     }
2448     if (appData.popupExitMessage) {
2449         XtSetValues(XtNameToWidget(menuBarWidget,
2450                                    "menuOptions.Popup Exit Message"), args, 1);
2451     }
2452     if (appData.popupMoveErrors) {
2453         XtSetValues(XtNameToWidget(menuBarWidget,
2454                                    "menuOptions.Popup Move Errors"), args, 1);
2455     }
2456     if (appData.premove) {
2457         XtSetValues(XtNameToWidget(menuBarWidget,
2458                                    "menuOptions.Premove"), args, 1);
2459     }
2460     if (appData.quietPlay) {
2461         XtSetValues(XtNameToWidget(menuBarWidget,
2462                                    "menuOptions.Quiet Play"), args, 1);
2463     }
2464     if (appData.showCoords) {
2465         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2466                     args, 1);
2467     }
2468     if (appData.hideThinkingFromHuman) {
2469         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2470                     args, 1);
2471     }
2472     if (appData.testLegality) {
2473         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2474                     args, 1);
2475     }
2476     if (saveSettingsOnExit) {
2477         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2478                     args, 1);
2479     }
2480
2481     /*
2482      * Create an icon.
2483      */
2484     ReadBitmap(&wIconPixmap, "icon_white.bm",
2485                icon_white_bits, icon_white_width, icon_white_height);
2486     ReadBitmap(&bIconPixmap, "icon_black.bm",
2487                icon_black_bits, icon_black_width, icon_black_height);
2488     iconPixmap = wIconPixmap;
2489     i = 0;
2490     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
2491     XtSetValues(shellWidget, args, i);
2492
2493     /*
2494      * Create a cursor for the board widget.
2495      */
2496     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2497     XChangeWindowAttributes(xDisplay, xBoardWindow,
2498                             CWCursor, &window_attributes);
2499
2500     /*
2501      * Inhibit shell resizing.
2502      */
2503     shellArgs[0].value = (XtArgVal) &w;
2504     shellArgs[1].value = (XtArgVal) &h;
2505     XtGetValues(shellWidget, shellArgs, 2);
2506     shellArgs[4].value = shellArgs[2].value = w;
2507     shellArgs[5].value = shellArgs[3].value = h;
2508     XtSetValues(shellWidget, &shellArgs[2], 4);
2509     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2510     marginH =  h - boardHeight;
2511
2512     CatchDeleteWindow(shellWidget, "QuitProc");
2513
2514     CreateGCs();
2515     CreateGrid();
2516 #if HAVE_LIBXPM
2517     if (appData.bitmapDirectory[0] != NULLCHAR) {
2518       CreatePieces();
2519     } else {
2520       CreateXPMPieces();
2521     }
2522 #else
2523     CreateXIMPieces();
2524     /* Create regular pieces */
2525     if (!useImages) CreatePieces();
2526 #endif
2527
2528     CreatePieceMenus();
2529
2530     if (appData.animate || appData.animateDragging)
2531       CreateAnimVars();
2532
2533     XtAugmentTranslations(formWidget,
2534                           XtParseTranslationTable(globalTranslations));
2535     XtAugmentTranslations(boardWidget,
2536                           XtParseTranslationTable(boardTranslations));
2537     XtAugmentTranslations(whiteTimerWidget,
2538                           XtParseTranslationTable(whiteTranslations));
2539     XtAugmentTranslations(blackTimerWidget,
2540                           XtParseTranslationTable(blackTranslations));
2541
2542     /* Why is the following needed on some versions of X instead
2543      * of a translation? */
2544     XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2545                       (XtEventHandler) EventProc, NULL);
2546     /* end why */
2547
2548     /* [AS] Restore layout */
2549     if( wpMoveHistory.visible ) {
2550       HistoryPopUp();
2551     }
2552
2553     if( wpEvalGraph.visible )
2554       {
2555         EvalGraphPopUp();
2556       };
2557
2558     if( wpEngineOutput.visible ) {
2559       EngineOutputPopUp();
2560     }
2561
2562     InitBackEnd2();
2563
2564     if (errorExitStatus == -1) {
2565         if (appData.icsActive) {
2566             /* We now wait until we see "login:" from the ICS before
2567                sending the logon script (problems with timestamp otherwise) */
2568             /*ICSInitScript();*/
2569             if (appData.icsInputBox) ICSInputBoxPopUp();
2570         }
2571
2572     #ifdef SIGWINCH
2573     signal(SIGWINCH, TermSizeSigHandler);
2574     #endif
2575         signal(SIGINT, IntSigHandler);
2576         signal(SIGTERM, IntSigHandler);
2577         if (*appData.cmailGameName != NULLCHAR) {
2578             signal(SIGUSR1, CmailSigHandler);
2579         }
2580     }
2581     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2582     InitPosition(TRUE);
2583     XtSetKeyboardFocus(shellWidget, formWidget);
2584
2585     XtAppMainLoop(appContext);
2586     if (appData.debugMode) fclose(debugFP); // [DM] debug
2587     return 0;
2588 }
2589
2590 void
2591 ShutDownFrontEnd()
2592 {
2593     if (appData.icsActive && oldICSInteractionTitle != NULL) {
2594         DisplayIcsInteractionTitle(oldICSInteractionTitle);
2595     }
2596     if (saveSettingsOnExit) SaveSettings(settingsFileName);
2597     unlink(gameCopyFilename);
2598     unlink(gamePasteFilename);
2599 }
2600
2601 RETSIGTYPE TermSizeSigHandler(int sig)
2602 {
2603     update_ics_width();
2604 }
2605
2606 RETSIGTYPE
2607 IntSigHandler(sig)
2608      int sig;
2609 {
2610     ExitEvent(sig);
2611 }
2612
2613 RETSIGTYPE
2614 CmailSigHandler(sig)
2615      int sig;
2616 {
2617     int dummy = 0;
2618     int error;
2619
2620     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
2621
2622     /* Activate call-back function CmailSigHandlerCallBack()             */
2623     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2624
2625     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2626 }
2627
2628 void
2629 CmailSigHandlerCallBack(isr, closure, message, count, error)
2630      InputSourceRef isr;
2631      VOIDSTAR closure;
2632      char *message;
2633      int count;
2634      int error;
2635 {
2636     BoardToTop();
2637     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
2638 }
2639 /**** end signal code ****/
2640
2641
2642 void
2643 ICSInitScript()
2644 {
2645     FILE *f;
2646     char buf[MSG_SIZ];
2647     char *p;
2648
2649     f = fopen(appData.icsLogon, "r");
2650     if (f == NULL) {
2651         p = getenv("HOME");
2652         if (p != NULL) {
2653           safeStrCpy(buf, p, sizeof(buf)/sizeof(buf[0]) );
2654           strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2655           strncat(buf, appData.icsLogon,  MSG_SIZ - strlen(buf) - 1);
2656           f = fopen(buf, "r");
2657         }
2658     }
2659
2660     if (f != NULL)
2661       ProcessICSInitScript(f);
2662 }
2663
2664 void
2665 ResetFrontEnd()
2666 {
2667     CommentPopDown();
2668     EditCommentPopDown();
2669     TagsPopDown();
2670     return;
2671 }
2672
2673 typedef struct {
2674     char *name;
2675     Boolean value;
2676 } Enables;
2677
2678 void
2679 GreyRevert(grey)
2680      Boolean grey;
2681 {
2682     Widget w;
2683     if (!menuBarWidget) return;
2684     w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
2685     if (w == NULL) {
2686       DisplayError("menuStep.Revert", 0);
2687     } else {
2688       XtSetSensitive(w, !grey);
2689     }
2690     w = XtNameToWidget(menuBarWidget, "menuStep.Annotate");
2691     if (w == NULL) {
2692       DisplayError("menuStep.Annotate", 0);
2693     } else {
2694       XtSetSensitive(w, !grey);
2695     }
2696 }
2697
2698 void
2699 SetMenuEnables(enab)
2700      Enables *enab;
2701 {
2702   Widget w;
2703   if (!menuBarWidget) return;
2704   while (enab->name != NULL) {
2705     w = XtNameToWidget(menuBarWidget, enab->name);
2706     if (w == NULL) {
2707       DisplayError(enab->name, 0);
2708     } else {
2709       XtSetSensitive(w, enab->value);
2710     }
2711     enab++;
2712   }
2713 }
2714
2715 Enables icsEnables[] = {
2716     { "menuFile.Mail Move", False },
2717     { "menuFile.Reload CMail Message", False },
2718     { "menuMode.Machine Black", False },
2719     { "menuMode.Machine White", False },
2720     { "menuMode.Analysis Mode", False },
2721     { "menuMode.Analyze File", False },
2722     { "menuMode.Two Machines", False },
2723 #ifndef ZIPPY
2724     { "menuHelp.Hint", False },
2725     { "menuHelp.Book", False },
2726     { "menuStep.Move Now", False },
2727     { "menuOptions.Periodic Updates", False },
2728     { "menuOptions.Hide Thinking", False },
2729     { "menuOptions.Ponder Next Move", False },
2730 #endif
2731     { "menuStep.Annotate", False },
2732     { NULL, False }
2733 };
2734
2735 Enables ncpEnables[] = {
2736     { "menuFile.Mail Move", False },
2737     { "menuFile.Reload CMail Message", False },
2738     { "menuMode.Machine White", False },
2739     { "menuMode.Machine Black", False },
2740     { "menuMode.Analysis Mode", False },
2741     { "menuMode.Analyze File", False },
2742     { "menuMode.Two Machines", False },
2743     { "menuMode.ICS Client", False },
2744     { "menuMode.ICS Input Box", False },
2745     { "Action", False },
2746     { "menuStep.Revert", False },
2747     { "menuStep.Annotate", False },
2748     { "menuStep.Move Now", False },
2749     { "menuStep.Retract Move", False },
2750     { "menuOptions.Auto Comment", False },
2751     { "menuOptions.Auto Flag", False },
2752     { "menuOptions.Auto Flip View", False },
2753     { "menuOptions.Auto Observe", False },
2754     { "menuOptions.Auto Raise Board", False },
2755     { "menuOptions.Get Move List", False },
2756     { "menuOptions.ICS Alarm", False },
2757     { "menuOptions.Move Sound", False },
2758     { "menuOptions.Quiet Play", False },
2759     { "menuOptions.Hide Thinking", False },
2760     { "menuOptions.Periodic Updates", False },
2761     { "menuOptions.Ponder Next Move", False },
2762     { "menuHelp.Hint", False },
2763     { "menuHelp.Book", False },
2764     { NULL, False }
2765 };
2766
2767 Enables gnuEnables[] = {
2768     { "menuMode.ICS Client", False },
2769     { "menuMode.ICS Input Box", False },
2770     { "menuAction.Accept", False },
2771     { "menuAction.Decline", False },
2772     { "menuAction.Rematch", False },
2773     { "menuAction.Adjourn", False },
2774     { "menuAction.Stop Examining", False },
2775     { "menuAction.Stop Observing", False },
2776     { "menuAction.Upload to Examine", False },
2777     { "menuStep.Revert", False },
2778     { "menuStep.Annotate", False },
2779     { "menuOptions.Auto Comment", False },
2780     { "menuOptions.Auto Observe", False },
2781     { "menuOptions.Auto Raise Board", False },
2782     { "menuOptions.Get Move List", False },
2783     { "menuOptions.Premove", False },
2784     { "menuOptions.Quiet Play", False },
2785
2786     /* The next two options rely on SetCmailMode being called *after*    */
2787     /* SetGNUMode so that when GNU is being used to give hints these     */
2788     /* menu options are still available                                  */
2789
2790     { "menuFile.Mail Move", False },
2791     { "menuFile.Reload CMail Message", False },
2792     { NULL, False }
2793 };
2794
2795 Enables cmailEnables[] = {
2796     { "Action", True },
2797     { "menuAction.Call Flag", False },
2798     { "menuAction.Draw", True },
2799     { "menuAction.Adjourn", False },
2800     { "menuAction.Abort", False },
2801     { "menuAction.Stop Observing", False },
2802     { "menuAction.Stop Examining", False },
2803     { "menuFile.Mail Move", True },
2804     { "menuFile.Reload CMail Message", True },
2805     { NULL, False }
2806 };
2807
2808 Enables trainingOnEnables[] = {
2809   { "menuMode.Edit Comment", False },
2810   { "menuMode.Pause", False },
2811   { "menuStep.Forward", False },
2812   { "menuStep.Backward", False },
2813   { "menuStep.Forward to End", False },
2814   { "menuStep.Back to Start", False },
2815   { "menuStep.Move Now", False },
2816   { "menuStep.Truncate Game", False },
2817   { NULL, False }
2818 };
2819
2820 Enables trainingOffEnables[] = {
2821   { "menuMode.Edit Comment", True },
2822   { "menuMode.Pause", True },
2823   { "menuStep.Forward", True },
2824   { "menuStep.Backward", True },
2825   { "menuStep.Forward to End", True },
2826   { "menuStep.Back to Start", True },
2827   { "menuStep.Move Now", True },
2828   { "menuStep.Truncate Game", True },
2829   { NULL, False }
2830 };
2831
2832 Enables machineThinkingEnables[] = {
2833   { "menuFile.Load Game", False },
2834   { "menuFile.Load Next Game", False },
2835   { "menuFile.Load Previous Game", False },
2836   { "menuFile.Reload Same Game", False },
2837   { "menuFile.Paste Game", False },
2838   { "menuFile.Load Position", False },
2839   { "menuFile.Load Next Position", False },
2840   { "menuFile.Load Previous Position", False },
2841   { "menuFile.Reload Same Position", False },
2842   { "menuFile.Paste Position", False },
2843   { "menuMode.Machine White", False },
2844   { "menuMode.Machine Black", False },
2845   { "menuMode.Two Machines", False },
2846   { "menuStep.Retract Move", False },
2847   { NULL, False }
2848 };
2849
2850 Enables userThinkingEnables[] = {
2851   { "menuFile.Load Game", True },
2852   { "menuFile.Load Next Game", True },
2853   { "menuFile.Load Previous Game", True },
2854   { "menuFile.Reload Same Game", True },
2855   { "menuFile.Paste Game", True },
2856   { "menuFile.Load Position", True },
2857   { "menuFile.Load Next Position", True },
2858   { "menuFile.Load Previous Position", True },
2859   { "menuFile.Reload Same Position", True },
2860   { "menuFile.Paste Position", True },
2861   { "menuMode.Machine White", True },
2862   { "menuMode.Machine Black", True },
2863   { "menuMode.Two Machines", True },
2864   { "menuStep.Retract Move", True },
2865   { NULL, False }
2866 };
2867
2868 void SetICSMode()
2869 {
2870   SetMenuEnables(icsEnables);
2871
2872 #if ZIPPY
2873   if (appData.zippyPlay && !appData.noChessProgram)   /* [DM] icsEngineAnalyze */
2874      XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2875 #endif
2876 }
2877
2878 void
2879 SetNCPMode()
2880 {
2881   SetMenuEnables(ncpEnables);
2882 }
2883
2884 void
2885 SetGNUMode()
2886 {
2887   SetMenuEnables(gnuEnables);
2888 }
2889
2890 void
2891 SetCmailMode()
2892 {
2893   SetMenuEnables(cmailEnables);
2894 }
2895
2896 void
2897 SetTrainingModeOn()
2898 {
2899   SetMenuEnables(trainingOnEnables);
2900   if (appData.showButtonBar) {
2901     XtSetSensitive(buttonBarWidget, False);
2902   }
2903   CommentPopDown();
2904 }
2905
2906 void
2907 SetTrainingModeOff()
2908 {
2909   SetMenuEnables(trainingOffEnables);
2910   if (appData.showButtonBar) {
2911     XtSetSensitive(buttonBarWidget, True);
2912   }
2913 }
2914
2915 void
2916 SetUserThinkingEnables()
2917 {
2918   if (appData.noChessProgram) return;
2919   SetMenuEnables(userThinkingEnables);
2920 }
2921
2922 void
2923 SetMachineThinkingEnables()
2924 {
2925   if (appData.noChessProgram) return;
2926   SetMenuEnables(machineThinkingEnables);
2927   switch (gameMode) {
2928   case MachinePlaysBlack:
2929   case MachinePlaysWhite:
2930   case TwoMachinesPlay:
2931     XtSetSensitive(XtNameToWidget(menuBarWidget,
2932                                   ModeToWidgetName(gameMode)), True);
2933     break;
2934   default:
2935     break;
2936   }
2937 }
2938
2939 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2940 #define HISTORY_SIZE 64\r
2941 static char *history[HISTORY_SIZE];\r
2942 int histIn = 0, histP = 0;\r
2943 \r
2944 void\r
2945 SaveInHistory(char *cmd)\r
2946 {\r
2947   if (history[histIn] != NULL) {\r
2948     free(history[histIn]);\r
2949     history[histIn] = NULL;\r
2950   }\r
2951   if (*cmd == NULLCHAR) return;\r
2952   history[histIn] = StrSave(cmd);\r
2953   histIn = (histIn + 1) % HISTORY_SIZE;\r
2954   if (history[histIn] != NULL) {\r
2955     free(history[histIn]);\r
2956     history[histIn] = NULL;\r
2957   }\r
2958   histP = histIn;\r
2959 }\r
2960 \r
2961 char *\r
2962 PrevInHistory(char *cmd)\r
2963 {\r
2964   int newhp;\r
2965   if (histP == histIn) {\r
2966     if (history[histIn] != NULL) free(history[histIn]);\r
2967     history[histIn] = StrSave(cmd);\r
2968   }\r
2969   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
2970   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
2971   histP = newhp;\r
2972   return history[histP];\r
2973 }\r
2974 \r
2975 char *\r
2976 NextInHistory()\r
2977 {\r
2978   if (histP == histIn) return NULL;\r
2979   histP = (histP + 1) % HISTORY_SIZE;\r
2980   return history[histP];   \r
2981 }
2982 // end of borrowed code\r
2983 \r
2984 #define Abs(n) ((n)<0 ? -(n) : (n))
2985
2986 /*
2987  * Find a font that matches "pattern" that is as close as
2988  * possible to the targetPxlSize.  Prefer fonts that are k
2989  * pixels smaller to fonts that are k pixels larger.  The
2990  * pattern must be in the X Consortium standard format,
2991  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2992  * The return value should be freed with XtFree when no
2993  * longer needed.
2994  */
2995 char *
2996 FindFont(pattern, targetPxlSize)
2997      char *pattern;
2998      int targetPxlSize;
2999 {
3000     char **fonts, *p, *best, *scalable, *scalableTail;
3001     int i, j, nfonts, minerr, err, pxlSize;
3002
3003 #ifdef ENABLE_NLS
3004     char **missing_list;
3005     int missing_count;
3006     char *def_string, *base_fnt_lst, strInt[3];
3007     XFontSet fntSet;
3008     XFontStruct **fnt_list;
3009
3010     base_fnt_lst = calloc(1, strlen(pattern) + 3);
3011     snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
3012     p = strstr(pattern, "--");
3013     strncpy(base_fnt_lst, pattern, p - pattern + 2);
3014     strcat(base_fnt_lst, strInt);
3015     strcat(base_fnt_lst, strchr(p + 2, '-'));
3016
3017     if ((fntSet = XCreateFontSet(xDisplay,
3018                                  base_fnt_lst,
3019                                  &missing_list,
3020                                  &missing_count,
3021                                  &def_string)) == NULL) {
3022
3023        fprintf(stderr, _("Unable to create font set.\n"));
3024        exit (2);
3025     }
3026
3027     nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
3028 #else
3029     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3030     if (nfonts < 1) {
3031         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3032                 programName, pattern);
3033         exit(2);
3034     }
3035 #endif
3036
3037     best = fonts[0];
3038     scalable = NULL;
3039     minerr = 999999;
3040     for (i=0; i<nfonts; i++) {
3041         j = 0;
3042         p = fonts[i];
3043         if (*p != '-') continue;
3044         while (j < 7) {
3045             if (*p == NULLCHAR) break;
3046             if (*p++ == '-') j++;
3047         }
3048         if (j < 7) continue;
3049         pxlSize = atoi(p);
3050         if (pxlSize == 0) {
3051             scalable = fonts[i];
3052             scalableTail = p;
3053         } else {
3054             err = pxlSize - targetPxlSize;
3055             if (Abs(err) < Abs(minerr) ||
3056                 (minerr > 0 && err < 0 && -err == minerr)) {
3057                 best = fonts[i];
3058                 minerr = err;
3059             }
3060         }
3061     }
3062     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3063         /* If the error is too big and there is a scalable font,
3064            use the scalable font. */
3065         int headlen = scalableTail - scalable;
3066         p = (char *) XtMalloc(strlen(scalable) + 10);
3067         while (isdigit(*scalableTail)) scalableTail++;
3068         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3069     } else {
3070         p = (char *) XtMalloc(strlen(best) + 2);
3071         safeStrCpy(p, best, strlen(best)+1 );
3072     }
3073     if (appData.debugMode) {
3074         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
3075                 pattern, targetPxlSize, p);
3076     }
3077 #ifdef ENABLE_NLS
3078     if (missing_count > 0)
3079        XFreeStringList(missing_list);
3080     XFreeFontSet(xDisplay, fntSet);
3081 #else
3082      XFreeFontNames(fonts);
3083 #endif
3084     return p;
3085 }
3086
3087 void CreateGCs()
3088 {
3089     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3090       | GCBackground | GCFunction | GCPlaneMask;
3091     XGCValues gc_values;
3092     GC copyInvertedGC;
3093
3094     gc_values.plane_mask = AllPlanes;
3095     gc_values.line_width = lineGap;
3096     gc_values.line_style = LineSolid;
3097     gc_values.function = GXcopy;
3098
3099     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3100     gc_values.background = XBlackPixel(xDisplay, xScreen);
3101     lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3102
3103     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3104     gc_values.background = XWhitePixel(xDisplay, xScreen);
3105     coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3106     XSetFont(xDisplay, coordGC, coordFontID);
3107
3108     // [HGM] make font for holdings counts (white on black0
3109     gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3110     gc_values.background = XBlackPixel(xDisplay, xScreen);
3111     countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3112     XSetFont(xDisplay, countGC, countFontID);
3113
3114     if (appData.monoMode) {
3115         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3116         gc_values.background = XWhitePixel(xDisplay, xScreen);
3117         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3118
3119         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3120         gc_values.background = XBlackPixel(xDisplay, xScreen);
3121         lightSquareGC = wbPieceGC
3122           = XtGetGC(shellWidget, value_mask, &gc_values);
3123
3124         gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3125         gc_values.background = XWhitePixel(xDisplay, xScreen);
3126         darkSquareGC = bwPieceGC
3127           = XtGetGC(shellWidget, value_mask, &gc_values);
3128
3129         if (DefaultDepth(xDisplay, xScreen) == 1) {
3130             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3131             gc_values.function = GXcopyInverted;
3132             copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3133             gc_values.function = GXcopy;
3134             if (XBlackPixel(xDisplay, xScreen) == 1) {
3135                 bwPieceGC = darkSquareGC;
3136                 wbPieceGC = copyInvertedGC;
3137             } else {
3138                 bwPieceGC = copyInvertedGC;
3139                 wbPieceGC = lightSquareGC;
3140             }
3141         }
3142     } else {
3143         gc_values.foreground = highlightSquareColor;
3144         gc_values.background = highlightSquareColor;
3145         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3146
3147         gc_values.foreground = premoveHighlightColor;
3148         gc_values.background = premoveHighlightColor;
3149         prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3150
3151         gc_values.foreground = lightSquareColor;
3152         gc_values.background = darkSquareColor;
3153         lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3154
3155         gc_values.foreground = darkSquareColor;
3156         gc_values.background = lightSquareColor;
3157         darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3158
3159         gc_values.foreground = jailSquareColor;
3160         gc_values.background = jailSquareColor;
3161         jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3162
3163         gc_values.foreground = whitePieceColor;
3164         gc_values.background = darkSquareColor;
3165         wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3166
3167         gc_values.foreground = whitePieceColor;
3168         gc_values.background = lightSquareColor;
3169         wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3170
3171         gc_values.foreground = whitePieceColor;
3172         gc_values.background = jailSquareColor;
3173         wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3174
3175         gc_values.foreground = blackPieceColor;
3176         gc_values.background = darkSquareColor;
3177         bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3178
3179         gc_values.foreground = blackPieceColor;
3180         gc_values.background = lightSquareColor;
3181         blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3182
3183         gc_values.foreground = blackPieceColor;
3184         gc_values.background = jailSquareColor;
3185         bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3186     }
3187 }
3188
3189 void loadXIM(xim, xmask, filename, dest, mask)
3190      XImage *xim;
3191      XImage *xmask;
3192      char *filename;
3193      Pixmap *dest;
3194      Pixmap *mask;
3195 {
3196     int x, y, w, h, p;
3197     FILE *fp;
3198     Pixmap temp;
3199     XGCValues   values;
3200     GC maskGC;
3201
3202     fp = fopen(filename, "rb");
3203     if (!fp) {
3204         fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3205         exit(1);
3206     }
3207
3208     w = fgetc(fp);
3209     h = fgetc(fp);
3210
3211     for (y=0; y<h; ++y) {
3212         for (x=0; x<h; ++x) {
3213             p = fgetc(fp);
3214
3215             switch (p) {
3216               case 0:
3217                 XPutPixel(xim, x, y, blackPieceColor);
3218                 if (xmask)
3219                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3220                 break;
3221               case 1:
3222                 XPutPixel(xim, x, y, darkSquareColor);
3223                 if (xmask)
3224                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3225                 break;
3226               case 2:
3227                 XPutPixel(xim, x, y, whitePieceColor);
3228                 if (xmask)
3229                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3230                 break;
3231               case 3:
3232                 XPutPixel(xim, x, y, lightSquareColor);
3233                 if (xmask)
3234                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3235                 break;
3236             }
3237         }
3238     }
3239
3240     /* create Pixmap of piece */
3241     *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3242                           w, h, xim->depth);
3243     XPutImage(xDisplay, *dest, lightSquareGC, xim,
3244               0, 0, 0, 0, w, h);
3245
3246     /* create Pixmap of clipmask
3247        Note: We assume the white/black pieces have the same
3248              outline, so we make only 6 masks. This is okay
3249              since the XPM clipmask routines do the same. */
3250     if (xmask) {
3251       temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3252                             w, h, xim->depth);
3253       XPutImage(xDisplay, temp, lightSquareGC, xmask,
3254               0, 0, 0, 0, w, h);
3255
3256       /* now create the 1-bit version */
3257       *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3258                           w, h, 1);
3259
3260       values.foreground = 1;
3261       values.background = 0;
3262
3263       /* Don't use XtGetGC, not read only */
3264       maskGC = XCreateGC(xDisplay, *mask,
3265                     GCForeground | GCBackground, &values);
3266       XCopyPlane(xDisplay, temp, *mask, maskGC,
3267                   0, 0, squareSize, squareSize, 0, 0, 1);
3268       XFreePixmap(xDisplay, temp);
3269     }
3270 }
3271
3272
3273 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3274
3275 void CreateXIMPieces()
3276 {
3277     int piece, kind;
3278     char buf[MSG_SIZ];
3279     u_int ss;
3280     static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3281     XImage *ximtemp;
3282
3283     ss = squareSize;
3284
3285     /* The XSynchronize calls were copied from CreatePieces.
3286        Not sure if needed, but can't hurt */
3287     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3288                                      buffering bug */
3289
3290     /* temp needed by loadXIM() */
3291     ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3292                  0, 0, ss, ss, AllPlanes, XYPixmap);
3293
3294     if (strlen(appData.pixmapDirectory) == 0) {
3295       useImages = 0;
3296     } else {
3297         useImages = 1;
3298         if (appData.monoMode) {
3299           DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3300                             0, 2);
3301           ExitEvent(2);
3302         }
3303         fprintf(stderr, _("\nLoading XIMs...\n"));
3304         /* Load pieces */
3305         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3306             fprintf(stderr, "%d", piece+1);
3307             for (kind=0; kind<4; kind++) {
3308                 fprintf(stderr, ".");
3309                 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3310                         ExpandPathName(appData.pixmapDirectory),
3311                         piece <= (int) WhiteKing ? "" : "w",
3312                         pieceBitmapNames[piece],
3313                         ximkind[kind], ss);
3314                 ximPieceBitmap[kind][piece] =
3315                   XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3316                             0, 0, ss, ss, AllPlanes, XYPixmap);
3317                 if (appData.debugMode)
3318                   fprintf(stderr, _("(File:%s:) "), buf);
3319                 loadXIM(ximPieceBitmap[kind][piece],
3320                         ximtemp, buf,
3321                         &(xpmPieceBitmap2[kind][piece]),
3322                         &(ximMaskPm2[piece]));
3323                 if(piece <= (int)WhiteKing)
3324                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3325             }
3326             fprintf(stderr," ");
3327         }
3328         /* Load light and dark squares */
3329         /* If the LSQ and DSQ pieces don't exist, we will
3330            draw them with solid squares. */
3331         snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3332         if (access(buf, 0) != 0) {
3333             useImageSqs = 0;
3334         } else {
3335             useImageSqs = 1;
3336             fprintf(stderr, _("light square "));
3337             ximLightSquare=
3338               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3339                         0, 0, ss, ss, AllPlanes, XYPixmap);
3340             if (appData.debugMode)
3341               fprintf(stderr, _("(File:%s:) "), buf);
3342
3343             loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3344             fprintf(stderr, _("dark square "));
3345             snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3346                     ExpandPathName(appData.pixmapDirectory), ss);
3347             if (appData.debugMode)
3348               fprintf(stderr, _("(File:%s:) "), buf);
3349             ximDarkSquare=
3350               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3351                         0, 0, ss, ss, AllPlanes, XYPixmap);
3352             loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3353             xpmJailSquare = xpmLightSquare;
3354         }
3355         fprintf(stderr, _("Done.\n"));
3356     }
3357     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3358 }
3359
3360 #if HAVE_LIBXPM
3361 void CreateXPMPieces()
3362 {
3363     int piece, kind, r;
3364     char buf[MSG_SIZ];
3365     u_int ss = squareSize;
3366     XpmAttributes attr;
3367     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3368     XpmColorSymbol symbols[4];
3369
3370     /* The XSynchronize calls were copied from CreatePieces.
3371        Not sure if needed, but can't hurt */
3372     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3373
3374     /* Setup translations so piece colors match square colors */
3375     symbols[0].name = "light_piece";
3376     symbols[0].value = appData.whitePieceColor;
3377     symbols[1].name = "dark_piece";
3378     symbols[1].value = appData.blackPieceColor;
3379     symbols[2].name = "light_square";
3380     symbols[2].value = appData.lightSquareColor;
3381     symbols[3].name = "dark_square";
3382     symbols[3].value = appData.darkSquareColor;
3383
3384     attr.valuemask = XpmColorSymbols;
3385     attr.colorsymbols = symbols;
3386     attr.numsymbols = 4;
3387
3388     if (appData.monoMode) {
3389       DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3390                         0, 2);
3391       ExitEvent(2);
3392     }
3393     if (strlen(appData.pixmapDirectory) == 0) {
3394         XpmPieces* pieces = builtInXpms;
3395         useImages = 1;
3396         /* Load pieces */
3397         while (pieces->size != squareSize && pieces->size) pieces++;
3398         if (!pieces->size) {
3399           fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3400           exit(1);
3401         }
3402         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3403             for (kind=0; kind<4; kind++) {
3404
3405                 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3406                                                pieces->xpm[piece][kind],
3407                                                &(xpmPieceBitmap2[kind][piece]),
3408                                                NULL, &attr)) != 0) {
3409                   fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3410                           r, buf);
3411                   exit(1);
3412                 }
3413                 if(piece <= (int) WhiteKing)
3414                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3415             }
3416         }
3417         useImageSqs = 0;
3418         xpmJailSquare = xpmLightSquare;
3419     } else {
3420         useImages = 1;
3421
3422         fprintf(stderr, _("\nLoading XPMs...\n"));
3423
3424         /* Load pieces */
3425         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3426             fprintf(stderr, "%d ", piece+1);
3427             for (kind=0; kind<4; kind++) {
3428               snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3429                         ExpandPathName(appData.pixmapDirectory),
3430                         piece > (int) WhiteKing ? "w" : "",
3431                         pieceBitmapNames[piece],
3432                         xpmkind[kind], ss);
3433                 if (appData.debugMode) {
3434                     fprintf(stderr, _("(File:%s:) "), buf);
3435                 }
3436                 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3437                                            &(xpmPieceBitmap2[kind][piece]),
3438                                            NULL, &attr)) != 0) {
3439                     if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3440                       // [HGM] missing: read of unorthodox piece failed; substitute King.
3441                       snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3442                                 ExpandPathName(appData.pixmapDirectory),
3443                                 xpmkind[kind], ss);
3444                         if (appData.debugMode) {
3445                             fprintf(stderr, _("(Replace by File:%s:) "), buf);
3446                         }
3447                         r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3448                                                 &(xpmPieceBitmap2[kind][piece]),
3449                                                 NULL, &attr);
3450                     }
3451                     if (r != 0) {
3452                         fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3453                                 r, buf);
3454                         exit(1);
3455                     }
3456                 }
3457                 if(piece <= (int) WhiteKing)
3458                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3459             }
3460         }
3461         /* Load light and dark squares */
3462         /* If the LSQ and DSQ pieces don't exist, we will
3463            draw them with solid squares. */
3464         fprintf(stderr, _("light square "));
3465         snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3466         if (access(buf, 0) != 0) {
3467             useImageSqs = 0;
3468         } else {
3469             useImageSqs = 1;
3470             if (appData.debugMode)
3471               fprintf(stderr, _("(File:%s:) "), buf);
3472
3473             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3474                                        &xpmLightSquare, NULL, &attr)) != 0) {
3475                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3476                 exit(1);
3477             }
3478             fprintf(stderr, _("dark square "));
3479             snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3480                     ExpandPathName(appData.pixmapDirectory), ss);
3481             if (appData.debugMode) {
3482                 fprintf(stderr, _("(File:%s:) "), buf);
3483             }
3484             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3485                                        &xpmDarkSquare, NULL, &attr)) != 0) {
3486                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3487                 exit(1);
3488             }
3489         }
3490         xpmJailSquare = xpmLightSquare;
3491         fprintf(stderr, _("Done.\n"));
3492     }
3493     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3494                                       buffering bug */
3495 }
3496 #endif /* HAVE_LIBXPM */
3497
3498 #if HAVE_LIBXPM
3499 /* No built-in bitmaps */
3500 void CreatePieces()
3501 {
3502     int piece, kind;
3503     char buf[MSG_SIZ];
3504     u_int ss = squareSize;
3505
3506     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3507                                      buffering bug */
3508
3509     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3510         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3511           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3512                    pieceBitmapNames[piece],
3513                    ss, kind == SOLID ? 's' : 'o');
3514           ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3515           if(piece <= (int)WhiteKing)
3516             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3517         }
3518     }
3519
3520     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3521                                       buffering bug */
3522 }
3523 #else
3524 /* With built-in bitmaps */
3525 void CreatePieces()
3526 {
3527     BuiltInBits* bib = builtInBits;
3528     int piece, kind;
3529     char buf[MSG_SIZ];
3530     u_int ss = squareSize;
3531
3532     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3533                                      buffering bug */
3534
3535     while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3536
3537     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3538         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3539           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3540                    pieceBitmapNames[piece],
3541                    ss, kind == SOLID ? 's' : 'o');
3542           ReadBitmap(&pieceBitmap2[kind][piece], buf,
3543                      bib->bits[kind][piece], ss, ss);
3544           if(piece <= (int)WhiteKing)
3545             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3546         }
3547     }
3548
3549     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3550                                       buffering bug */
3551 }
3552 #endif
3553
3554 void ReadBitmap(pm, name, bits, wreq, hreq)
3555      Pixmap *pm;
3556      String name;
3557      unsigned char bits[];
3558      u_int wreq, hreq;
3559 {
3560     int x_hot, y_hot;
3561     u_int w, h;
3562     int errcode;
3563     char msg[MSG_SIZ], fullname[MSG_SIZ];
3564
3565     if (*appData.bitmapDirectory != NULLCHAR) {
3566       safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3567       strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3568       strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3569       errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3570                                 &w, &h, pm, &x_hot, &y_hot);
3571       fprintf(stderr, "load %s\n", name);
3572         if (errcode != BitmapSuccess) {
3573             switch (errcode) {
3574               case BitmapOpenFailed:
3575                 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3576                 break;
3577               case BitmapFileInvalid:
3578                 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3579                 break;
3580               case BitmapNoMemory:
3581                 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3582                         fullname);
3583                 break;
3584               default:
3585                 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3586                         errcode, fullname);
3587                 break;
3588             }
3589             fprintf(stderr, _("%s: %s...using built-in\n"),
3590                     programName, msg);
3591         } else if (w != wreq || h != hreq) {
3592             fprintf(stderr,
3593                     _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3594                     programName, fullname, w, h, wreq, hreq);
3595         } else {
3596             return;
3597         }
3598     }
3599     if (bits != NULL) {
3600         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3601                                     wreq, hreq);
3602     }
3603 }
3604
3605 void CreateGrid()
3606 {
3607     int i, j;
3608
3609     if (lineGap == 0) return;
3610
3611     /* [HR] Split this into 2 loops for non-square boards. */
3612
3613     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3614         gridSegments[i].x1 = 0;
3615         gridSegments[i].x2 =
3616           lineGap + BOARD_WIDTH * (squareSize + lineGap);
3617         gridSegments[i].y1 = gridSegments[i].y2
3618           = lineGap / 2 + (i * (squareSize + lineGap));
3619     }
3620
3621     for (j = 0; j < BOARD_WIDTH + 1; j++) {
3622         gridSegments[j + i].y1 = 0;
3623         gridSegments[j + i].y2 =
3624           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3625         gridSegments[j + i].x1 = gridSegments[j + i].x2
3626           = lineGap / 2 + (j * (squareSize + lineGap));
3627     }
3628 }
3629
3630 static void MenuBarSelect(w, addr, index)
3631      Widget w;
3632      caddr_t addr;
3633      caddr_t index;
3634 {
3635     XtActionProc proc = (XtActionProc) addr;
3636
3637     (proc)(NULL, NULL, NULL, NULL);
3638 }
3639
3640 void CreateMenuBarPopup(parent, name, mb)
3641      Widget parent;
3642      String name;
3643      Menu *mb;
3644 {
3645     int j;
3646     Widget menu, entry;
3647     MenuItem *mi;
3648     Arg args[16];
3649
3650     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3651                               parent, NULL, 0);
3652     j = 0;
3653     XtSetArg(args[j], XtNleftMargin, 20);   j++;
3654     XtSetArg(args[j], XtNrightMargin, 20);  j++;
3655     mi = mb->mi;
3656     while (mi->string != NULL) {
3657         if (strcmp(mi->string, "----") == 0) {
3658             entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3659                                           menu, args, j);
3660         } else {
3661           XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3662             entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
3663                                           menu, args, j+1);
3664             XtAddCallback(entry, XtNcallback,
3665                           (XtCallbackProc) MenuBarSelect,
3666                           (caddr_t) mi->proc);
3667         }
3668         mi++;
3669     }
3670 }
3671
3672 Widget CreateMenuBar(mb)
3673      Menu *mb;
3674 {
3675     int j;
3676     Widget anchor, menuBar;
3677     Arg args[16];
3678     char menuName[MSG_SIZ];
3679
3680     j = 0;
3681     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
3682     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3683     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3684     menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3685                              formWidget, args, j);
3686
3687     while (mb->name != NULL) {
3688         safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3689         strncat(menuName, mb->name, MSG_SIZ - strlen(menuName) - 1);
3690         j = 0;
3691         XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
3692         if (tinyLayout) {
3693             char shortName[2];
3694             shortName[0] = _(mb->name)[0];
3695             shortName[1] = NULLCHAR;
3696             XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3697         }
3698       else {
3699           XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3700       }
3701
3702         XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3703         anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3704                                        menuBar, args, j);
3705         CreateMenuBarPopup(menuBar, menuName, mb);
3706         mb++;
3707     }
3708     return menuBar;
3709 }
3710
3711 Widget CreateButtonBar(mi)
3712      MenuItem *mi;
3713 {
3714     int j;
3715     Widget button, buttonBar;
3716     Arg args[16];
3717
3718     j = 0;
3719     XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3720     if (tinyLayout) {
3721         XtSetArg(args[j], XtNhSpace, 0); j++;
3722     }
3723     XtSetArg(args[j], XtNborderWidth, 0); j++;
3724     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3725     buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3726                                formWidget, args, j);
3727
3728     while (mi->string != NULL) {
3729         j = 0;
3730         if (tinyLayout) {
3731             XtSetArg(args[j], XtNinternalWidth, 2); j++;
3732             XtSetArg(args[j], XtNborderWidth, 0); j++;
3733         }
3734       XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3735         button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3736                                        buttonBar, args, j);
3737         XtAddCallback(button, XtNcallback,
3738                       (XtCallbackProc) MenuBarSelect,
3739                       (caddr_t) mi->proc);
3740         mi++;
3741     }
3742     return buttonBar;
3743 }
3744
3745 Widget
3746 CreatePieceMenu(name, color)
3747      char *name;
3748      int color;
3749 {
3750     int i;
3751     Widget entry, menu;
3752     Arg args[16];
3753     ChessSquare selection;
3754
3755     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3756                               boardWidget, args, 0);
3757
3758     for (i = 0; i < PIECE_MENU_SIZE; i++) {
3759         String item = pieceMenuStrings[color][i];
3760
3761         if (strcmp(item, "----") == 0) {
3762             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3763                                           menu, NULL, 0);
3764         } else {
3765           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3766             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3767                                 menu, args, 1);
3768             selection = pieceMenuTranslation[color][i];
3769             XtAddCallback(entry, XtNcallback,
3770                           (XtCallbackProc) PieceMenuSelect,
3771                           (caddr_t) selection);
3772             if (selection == WhitePawn || selection == BlackPawn) {
3773                 XtSetArg(args[0], XtNpopupOnEntry, entry);
3774                 XtSetValues(menu, args, 1);
3775             }
3776         }
3777     }
3778     return menu;
3779 }
3780
3781 void
3782 CreatePieceMenus()
3783 {
3784     int i;
3785     Widget entry;
3786     Arg args[16];
3787     ChessSquare selection;
3788
3789     whitePieceMenu = CreatePieceMenu("menuW", 0);
3790     blackPieceMenu = CreatePieceMenu("menuB", 1);
3791
3792     XtRegisterGrabAction(PieceMenuPopup, True,
3793                          (unsigned)(ButtonPressMask|ButtonReleaseMask),
3794                          GrabModeAsync, GrabModeAsync);
3795
3796     XtSetArg(args[0], XtNlabel, _("Drop"));
3797     dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3798                                   boardWidget, args, 1);
3799     for (i = 0; i < DROP_MENU_SIZE; i++) {
3800         String item = dropMenuStrings[i];
3801
3802         if (strcmp(item, "----") == 0) {
3803             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3804                                           dropMenu, NULL, 0);
3805         } else {
3806           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3807             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3808                                 dropMenu, args, 1);
3809             selection = dropMenuTranslation[i];
3810             XtAddCallback(entry, XtNcallback,
3811                           (XtCallbackProc) DropMenuSelect,
3812                           (caddr_t) selection);
3813         }
3814     }
3815 }
3816
3817 void SetupDropMenu()
3818 {
3819     int i, j, count;
3820     char label[32];
3821     Arg args[16];
3822     Widget entry;
3823     char* p;
3824
3825     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3826         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3827         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3828                    dmEnables[i].piece);
3829         XtSetSensitive(entry, p != NULL || !appData.testLegality
3830                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3831                                        && !appData.icsActive));
3832         count = 0;
3833         while (p && *p++ == dmEnables[i].piece) count++;
3834         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
3835         j = 0;
3836         XtSetArg(args[j], XtNlabel, label); j++;
3837         XtSetValues(entry, args, j);
3838     }
3839 }
3840
3841 void PieceMenuPopup(w, event, params, num_params)
3842      Widget w;
3843      XEvent *event;
3844      String *params;
3845      Cardinal *num_params;
3846 {
3847     String whichMenu; int menuNr;
3848     if (event->type == ButtonRelease)
3849         menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3850     else if (event->type == ButtonPress)
3851         menuNr = RightClick(Press,   event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3852     switch(menuNr) {
3853       case 0: whichMenu = params[0]; break;
3854       case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3855       case 2:
3856       case -1: if (errorUp) ErrorPopDown();
3857       default: return;
3858     }
3859     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3860 }
3861
3862 static void PieceMenuSelect(w, piece, junk)
3863      Widget w;
3864      ChessSquare piece;
3865      caddr_t junk;
3866 {
3867     if (pmFromX < 0 || pmFromY < 0) return;
3868     EditPositionMenuEvent(piece, pmFromX, pmFromY);
3869 }
3870
3871 static void DropMenuSelect(w, piece, junk)
3872      Widget w;
3873      ChessSquare piece;
3874      caddr_t junk;
3875 {
3876     if (pmFromX < 0 || pmFromY < 0) return;
3877     DropMenuEvent(piece, pmFromX, pmFromY);
3878 }
3879
3880 void WhiteClock(w, event, prms, nprms)
3881      Widget w;
3882      XEvent *event;
3883      String *prms;
3884      Cardinal *nprms;
3885 {
3886     if (gameMode == EditPosition || gameMode == IcsExamining) {
3887         SetWhiteToPlayEvent();
3888     } else if (gameMode == IcsPlayingBlack || gameMode == MachinePlaysWhite) {
3889         CallFlagEvent();
3890     }
3891 }
3892
3893 void BlackClock(w, event, prms, nprms)
3894      Widget w;
3895      XEvent *event;
3896      String *prms;
3897      Cardinal *nprms;
3898 {
3899     if (gameMode == EditPosition || gameMode == IcsExamining) {
3900         SetBlackToPlayEvent();
3901     } else if (gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) {
3902         CallFlagEvent();
3903     }
3904 }
3905
3906
3907 /*
3908  * If the user selects on a border boundary, return -1; if off the board,
3909  *   return -2.  Otherwise map the event coordinate to the square.
3910  */
3911 int EventToSquare(x, limit)
3912      int x;
3913 {
3914     if (x <= 0)
3915       return -2;
3916     if (x < lineGap)
3917       return -1;
3918     x -= lineGap;
3919     if ((x % (squareSize + lineGap)) >= squareSize)
3920       return -1;
3921     x /= (squareSize + lineGap);
3922     if (x >= limit)
3923       return -2;
3924     return x;
3925 }
3926
3927 static void do_flash_delay(msec)
3928      unsigned long msec;
3929 {
3930     TimeDelay(msec);
3931 }
3932
3933 static void drawHighlight(file, rank, gc)
3934      int file, rank;
3935      GC gc;
3936 {
3937     int x, y;
3938
3939     if (lineGap == 0 || appData.blindfold) return;
3940
3941     if (flipView) {
3942         x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
3943           (squareSize + lineGap);
3944         y = lineGap/2 + rank * (squareSize + lineGap);
3945     } else {
3946         x = lineGap/2 + file * (squareSize + lineGap);
3947         y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
3948           (squareSize + lineGap);
3949     }
3950
3951     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3952                    squareSize+lineGap, squareSize+lineGap);
3953 }
3954
3955 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
3956 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
3957
3958 void
3959 SetHighlights(fromX, fromY, toX, toY)
3960      int fromX, fromY, toX, toY;
3961 {
3962     if (hi1X != fromX || hi1Y != fromY) {
3963         if (hi1X >= 0 && hi1Y >= 0) {
3964             drawHighlight(hi1X, hi1Y, lineGC);
3965         }
3966     } // [HGM] first erase both, then draw new!
3967     if (hi2X != toX || hi2Y != toY) {
3968         if (hi2X >= 0 && hi2Y >= 0) {
3969             drawHighlight(hi2X, hi2Y, lineGC);
3970         }
3971     }
3972     if (hi1X != fromX || hi1Y != fromY) {
3973         if (fromX >= 0 && fromY >= 0) {
3974             drawHighlight(fromX, fromY, highlineGC);
3975         }
3976     }
3977     if (hi2X != toX || hi2Y != toY) {
3978         if (toX >= 0 && toY >= 0) {
3979             drawHighlight(toX, toY, highlineGC);
3980         }
3981     }
3982     hi1X = fromX;
3983     hi1Y = fromY;
3984     hi2X = toX;
3985     hi2Y = toY;
3986 }
3987
3988 void
3989 ClearHighlights()
3990 {
3991     SetHighlights(-1, -1, -1, -1);
3992 }
3993
3994
3995 void
3996 SetPremoveHighlights(fromX, fromY, toX, toY)
3997      int fromX, fromY, toX, toY;
3998 {
3999     if (pm1X != fromX || pm1Y != fromY) {
4000         if (pm1X >= 0 && pm1Y >= 0) {
4001             drawHighlight(pm1X, pm1Y, lineGC);
4002         }
4003         if (fromX >= 0 && fromY >= 0) {
4004             drawHighlight(fromX, fromY, prelineGC);
4005         }
4006     }
4007     if (pm2X != toX || pm2Y != toY) {
4008         if (pm2X >= 0 && pm2Y >= 0) {
4009             drawHighlight(pm2X, pm2Y, lineGC);
4010         }
4011         if (toX >= 0 && toY >= 0) {
4012             drawHighlight(toX, toY, prelineGC);
4013         }
4014     }
4015     pm1X = fromX;
4016     pm1Y = fromY;
4017     pm2X = toX;
4018     pm2Y = toY;
4019 }
4020
4021 void
4022 ClearPremoveHighlights()
4023 {
4024   SetPremoveHighlights(-1, -1, -1, -1);
4025 }
4026
4027 static void BlankSquare(x, y, color, piece, dest)
4028      int x, y, color;
4029      ChessSquare piece;
4030      Drawable dest;
4031 {
4032     if (useImages && useImageSqs) {
4033         Pixmap pm;
4034         switch (color) {
4035           case 1: /* light */
4036             pm = xpmLightSquare;
4037             break;
4038           case 0: /* dark */
4039             pm = xpmDarkSquare;
4040             break;
4041           case 2: /* neutral */
4042           default:
4043             pm = xpmJailSquare;
4044             break;
4045         }
4046         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4047                   squareSize, squareSize, x, y);
4048     } else {
4049         GC gc;
4050         switch (color) {
4051           case 1: /* light */
4052             gc = lightSquareGC;
4053             break;
4054           case 0: /* dark */
4055             gc = darkSquareGC;
4056             break;
4057           case 2: /* neutral */
4058           default:
4059             gc = jailSquareGC;
4060             break;
4061         }
4062         XFillRectangle(xDisplay, dest, gc, x, y, squareSize, squareSize);
4063     }
4064 }
4065
4066 /*
4067    I split out the routines to draw a piece so that I could
4068    make a generic flash routine.
4069 */
4070 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4071      ChessSquare piece;
4072      int square_color, x, y;
4073      Drawable dest;
4074 {
4075     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4076     switch (square_color) {
4077       case 1: /* light */
4078       case 2: /* neutral */
4079       default:
4080         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4081                   ? *pieceToOutline(piece)
4082                   : *pieceToSolid(piece),
4083                   dest, bwPieceGC, 0, 0,
4084                   squareSize, squareSize, x, y);
4085         break;
4086       case 0: /* dark */
4087         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4088                   ? *pieceToSolid(piece)
4089                   : *pieceToOutline(piece),
4090                   dest, wbPieceGC, 0, 0,
4091                   squareSize, squareSize, x, y);
4092         break;
4093     }
4094 }
4095
4096 static void monoDrawPiece(piece, square_color, x, y, dest)
4097      ChessSquare piece;
4098      int square_color, x, y;
4099      Drawable dest;
4100 {
4101     switch (square_color) {
4102       case 1: /* light */
4103       case 2: /* neutral */
4104       default:
4105         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4106                    ? *pieceToOutline(piece)
4107                    : *pieceToSolid(piece),
4108                    dest, bwPieceGC, 0, 0,
4109                    squareSize, squareSize, x, y, 1);
4110         break;
4111       case 0: /* dark */
4112         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4113                    ? *pieceToSolid(piece)
4114                    : *pieceToOutline(piece),
4115                    dest, wbPieceGC, 0, 0,
4116                    squareSize, squareSize, x, y, 1);
4117         break;
4118     }
4119 }
4120
4121 static void colorDrawPiece(piece, square_color, x, y, dest)
4122      ChessSquare piece;
4123      int square_color, x, y;
4124      Drawable dest;
4125 {
4126     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4127     switch (square_color) {
4128       case 1: /* light */
4129         XCopyPlane(xDisplay, *pieceToSolid(piece),
4130                    dest, (int) piece < (int) BlackPawn
4131                    ? wlPieceGC : blPieceGC, 0, 0,
4132                    squareSize, squareSize, x, y, 1);
4133         break;
4134       case 0: /* dark */
4135         XCopyPlane(xDisplay, *pieceToSolid(piece),
4136                    dest, (int) piece < (int) BlackPawn
4137                    ? wdPieceGC : bdPieceGC, 0, 0,
4138                    squareSize, squareSize, x, y, 1);
4139         break;
4140       case 2: /* neutral */
4141       default:
4142         XCopyPlane(xDisplay, *pieceToSolid(piece),
4143                    dest, (int) piece < (int) BlackPawn
4144                    ? wjPieceGC : bjPieceGC, 0, 0,
4145                    squareSize, squareSize, x, y, 1);
4146         break;
4147     }
4148 }
4149
4150 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4151      ChessSquare piece;
4152      int square_color, x, y;
4153      Drawable dest;
4154 {
4155     int kind;
4156
4157     switch (square_color) {
4158       case 1: /* light */
4159       case 2: /* neutral */
4160       default:
4161         if ((int)piece < (int) BlackPawn) {
4162             kind = 0;
4163         } else {
4164             kind = 2;
4165             piece -= BlackPawn;
4166         }
4167         break;
4168       case 0: /* dark */
4169         if ((int)piece < (int) BlackPawn) {
4170             kind = 1;
4171         } else {
4172             kind = 3;
4173             piece -= BlackPawn;
4174         }
4175         break;
4176     }
4177     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4178               dest, wlPieceGC, 0, 0,
4179               squareSize, squareSize, x, y);
4180 }
4181
4182 typedef void (*DrawFunc)();
4183
4184 DrawFunc ChooseDrawFunc()
4185 {
4186     if (appData.monoMode) {
4187         if (DefaultDepth(xDisplay, xScreen) == 1) {
4188             return monoDrawPiece_1bit;
4189         } else {
4190             return monoDrawPiece;
4191         }
4192     } else {
4193         if (useImages)
4194           return colorDrawPieceImage;
4195         else
4196           return colorDrawPiece;
4197     }
4198 }
4199
4200 /* [HR] determine square color depending on chess variant. */
4201 static int SquareColor(row, column)
4202      int row, column;
4203 {
4204     int square_color;
4205
4206     if (gameInfo.variant == VariantXiangqi) {
4207         if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4208             square_color = 1;
4209         } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4210             square_color = 0;
4211         } else if (row <= 4) {
4212             square_color = 0;
4213         } else {
4214             square_color = 1;
4215         }
4216     } else {
4217         square_color = ((column + row) % 2) == 1;
4218     }
4219
4220     /* [hgm] holdings: next line makes all holdings squares light */
4221     if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4222
4223     return square_color;
4224 }
4225
4226 void DrawSquare(row, column, piece, do_flash)
4227      int row, column, do_flash;
4228      ChessSquare piece;
4229 {
4230     int square_color, x, y, direction, font_ascent, font_descent;
4231     int i;
4232     char string[2];
4233     XCharStruct overall;
4234     DrawFunc drawfunc;
4235     int flash_delay;
4236
4237     /* Calculate delay in milliseconds (2-delays per complete flash) */
4238     flash_delay = 500 / appData.flashRate;
4239
4240     if (flipView) {
4241         x = lineGap + ((BOARD_WIDTH-1)-column) *
4242           (squareSize + lineGap);
4243         y = lineGap + row * (squareSize + lineGap);
4244     } else {
4245         x = lineGap + column * (squareSize + lineGap);
4246         y = lineGap + ((BOARD_HEIGHT-1)-row) *
4247           (squareSize + lineGap);
4248     }
4249
4250     if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4251
4252     square_color = SquareColor(row, column);
4253
4254     if ( // [HGM] holdings: blank out area between board and holdings
4255                  column == BOARD_LEFT-1 ||  column == BOARD_RGHT
4256               || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4257                   || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4258                         BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
4259
4260                         // [HGM] print piece counts next to holdings
4261                         string[1] = NULLCHAR;
4262                         if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4263                             string[0] = '0' + piece;
4264                             XTextExtents(countFontStruct, string, 1, &direction,
4265                                  &font_ascent, &font_descent, &overall);
4266                             if (appData.monoMode) {
4267                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4268                                                  x + squareSize - overall.width - 2,
4269                                                  y + font_ascent + 1, string, 1);
4270                             } else {
4271                                 XDrawString(xDisplay, xBoardWindow, countGC,
4272                                             x + squareSize - overall.width - 2,
4273                                             y + font_ascent + 1, string, 1);
4274                             }
4275                         }
4276                         if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4277                             string[0] = '0' + piece;
4278                             XTextExtents(countFontStruct, string, 1, &direction,
4279                                          &font_ascent, &font_descent, &overall);
4280                             if (appData.monoMode) {
4281                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4282                                                  x + 2, y + font_ascent + 1, string, 1);
4283                             } else {
4284                                 XDrawString(xDisplay, xBoardWindow, countGC,
4285                                             x + 2, y + font_ascent + 1, string, 1);
4286                             }
4287                         }
4288     } else {
4289             if (piece == EmptySquare || appData.blindfold) {
4290                         BlankSquare(x, y, square_color, piece, xBoardWindow);
4291             } else {
4292                         drawfunc = ChooseDrawFunc();
4293                         if (do_flash && appData.flashCount > 0) {
4294                             for (i=0; i<appData.flashCount; ++i) {
4295
4296                                         drawfunc(piece, square_color, x, y, xBoardWindow);
4297                                         XSync(xDisplay, False);
4298                                         do_flash_delay(flash_delay);
4299
4300                                         BlankSquare(x, y, square_color, piece, xBoardWindow);
4301                                         XSync(xDisplay, False);
4302                                         do_flash_delay(flash_delay);
4303                             }
4304                         }
4305                         drawfunc(piece, square_color, x, y, xBoardWindow);
4306         }
4307         }
4308
4309     string[1] = NULLCHAR;
4310     if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4311                 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4312         string[0] = 'a' + column - BOARD_LEFT;
4313         XTextExtents(coordFontStruct, string, 1, &direction,
4314                      &font_ascent, &font_descent, &overall);
4315         if (appData.monoMode) {
4316             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4317                              x + squareSize - overall.width - 2,
4318                              y + squareSize - font_descent - 1, string, 1);
4319         } else {
4320             XDrawString(xDisplay, xBoardWindow, coordGC,
4321                         x + squareSize - overall.width - 2,
4322                         y + squareSize - font_descent - 1, string, 1);
4323         }
4324     }
4325     if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4326         string[0] = ONE + row;
4327         XTextExtents(coordFontStruct, string, 1, &direction,
4328                      &font_ascent, &font_descent, &overall);
4329         if (appData.monoMode) {
4330             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4331                              x + 2, y + font_ascent + 1, string, 1);
4332         } else {
4333             XDrawString(xDisplay, xBoardWindow, coordGC,
4334                         x + 2, y + font_ascent + 1, string, 1);
4335         }
4336     }
4337     if(!partnerUp && marker[row][column]) {
4338         XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4339                 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4340     }
4341 }
4342
4343
4344 /* Why is this needed on some versions of X? */
4345 void EventProc(widget, unused, event)
4346      Widget widget;
4347      caddr_t unused;
4348      XEvent *event;
4349 {
4350     if (!XtIsRealized(widget))
4351       return;
4352
4353     switch (event->type) {
4354       case Expose:
4355         if (event->xexpose.count > 0) return;  /* no clipping is done */
4356         XDrawPosition(widget, True, NULL);
4357         if(twoBoards) { // [HGM] dual: draw other board in other orientation
4358             flipView = !flipView; partnerUp = !partnerUp;
4359             XDrawPosition(widget, True, NULL);
4360             flipView = !flipView; partnerUp = !partnerUp;
4361         }
4362         break;
4363       case MotionNotify:
4364         if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;\r
4365       default:
4366         return;
4367     }
4368 }
4369 /* end why */
4370
4371 void DrawPosition(fullRedraw, board)
4372      /*Boolean*/int fullRedraw;
4373      Board board;
4374 {
4375     XDrawPosition(boardWidget, fullRedraw, board);
4376 }
4377
4378 /* Returns 1 if there are "too many" differences between b1 and b2
4379    (i.e. more than 1 move was made) */
4380 static int too_many_diffs(b1, b2)
4381      Board b1, b2;
4382 {
4383     int i, j;
4384     int c = 0;
4385
4386     for (i=0; i<BOARD_HEIGHT; ++i) {
4387         for (j=0; j<BOARD_WIDTH; ++j) {
4388             if (b1[i][j] != b2[i][j]) {
4389                 if (++c > 4)    /* Castling causes 4 diffs */
4390                   return 1;
4391             }
4392         }
4393     }
4394
4395     return 0;
4396 }
4397
4398 /* Matrix describing castling maneuvers */
4399 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4400 static int castling_matrix[4][5] = {
4401     { 0, 0, 4, 3, 2 },          /* 0-0-0, white */
4402     { 0, 7, 4, 5, 6 },          /* 0-0,   white */
4403     { 7, 0, 4, 3, 2 },          /* 0-0-0, black */
4404     { 7, 7, 4, 5, 6 }           /* 0-0,   black */
4405 };
4406
4407 /* Checks whether castling occurred. If it did, *rrow and *rcol
4408    are set to the destination (row,col) of the rook that moved.
4409
4410    Returns 1 if castling occurred, 0 if not.
4411
4412    Note: Only handles a max of 1 castling move, so be sure
4413    to call too_many_diffs() first.
4414    */
4415 static int check_castle_draw(newb, oldb, rrow, rcol)
4416      Board newb, oldb;
4417      int *rrow, *rcol;
4418 {
4419     int i, *r, j;
4420     int match;
4421
4422     /* For each type of castling... */
4423     for (i=0; i<4; ++i) {
4424         r = castling_matrix[i];
4425
4426         /* Check the 4 squares involved in the castling move */
4427         match = 0;
4428         for (j=1; j<=4; ++j) {
4429             if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4430                 match = 1;
4431                 break;
4432             }
4433         }
4434
4435         if (!match) {
4436             /* All 4 changed, so it must be a castling move */
4437             *rrow = r[0];
4438             *rcol = r[3];
4439             return 1;
4440         }
4441     }
4442     return 0;
4443 }
4444
4445 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4446 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4447 {
4448       XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4449 }
4450
4451 void DrawSeekBackground( int left, int top, int right, int bottom )
4452 {
4453     XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4454 }
4455
4456 void DrawSeekText(char *buf, int x, int y)
4457 {
4458     XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4459 }
4460
4461 void DrawSeekDot(int x, int y, int colorNr)
4462 {
4463     int square = colorNr & 0x80;
4464     GC color;
4465     colorNr &= 0x7F;
4466     color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4467     if(square)
4468         XFillRectangle(xDisplay, xBoardWindow, color,
4469                 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4470     else
4471         XFillArc(xDisplay, xBoardWindow, color,
4472                 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4473 }
4474
4475 static int damage[2][BOARD_RANKS][BOARD_FILES];
4476
4477 /*
4478  * event handler for redrawing the board
4479  */
4480 void XDrawPosition(w, repaint, board)
4481      Widget w;
4482      /*Boolean*/int repaint;
4483      Board board;
4484 {
4485     int i, j, do_flash;
4486     static int lastFlipView = 0;
4487     static int lastBoardValid[2] = {0, 0};
4488     static Board lastBoard[2];
4489     Arg args[16];
4490     int rrow, rcol;
4491     int nr = twoBoards*partnerUp;
4492
4493     if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4494
4495     if (board == NULL) {
4496         if (!lastBoardValid[nr]) return;
4497         board = lastBoard[nr];
4498     }
4499     if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4500         XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4501         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
4502                     args, 1);
4503     }
4504
4505     /*
4506      * It would be simpler to clear the window with XClearWindow()
4507      * but this causes a very distracting flicker.
4508      */
4509
4510     if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4511
4512         /* If too much changes (begin observing new game, etc.), don't
4513            do flashing */
4514         do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4515
4516         /* Special check for castling so we don't flash both the king
4517            and the rook (just flash the king). */
4518         if (do_flash) {
4519             if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4520                 /* Draw rook with NO flashing. King will be drawn flashing later */
4521                 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4522                 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4523             }
4524         }
4525
4526         /* First pass -- Draw (newly) empty squares and repair damage.
4527            This prevents you from having a piece show up twice while it
4528            is flashing on its new square */
4529         for (i = 0; i < BOARD_HEIGHT; i++)
4530           for (j = 0; j < BOARD_WIDTH; j++)
4531             if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4532                 || damage[nr][i][j]) {
4533                 DrawSquare(i, j, board[i][j], 0);
4534                 damage[nr][i][j] = False;
4535             }
4536
4537         /* Second pass -- Draw piece(s) in new position and flash them */
4538         for (i = 0; i < BOARD_HEIGHT; i++)
4539           for (j = 0; j < BOARD_WIDTH; j++)
4540             if (board[i][j] != lastBoard[nr][i][j]) {
4541                 DrawSquare(i, j, board[i][j], do_flash);
4542             }
4543     } else {
4544         if (lineGap > 0)
4545           XDrawSegments(xDisplay, xBoardWindow, lineGC,
4546                         twoBoards & partnerUp ? secondSegments : // [HGM] dual
4547                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4548
4549         for (i = 0; i < BOARD_HEIGHT; i++)
4550           for (j = 0; j < BOARD_WIDTH; j++) {
4551               DrawSquare(i, j, board[i][j], 0);
4552               damage[nr][i][j] = False;
4553           }
4554     }
4555
4556     CopyBoard(lastBoard[nr], board);
4557     lastBoardValid[nr] = 1;
4558   if(nr == 0) { // [HGM] dual: no highlights on second board yet
4559     lastFlipView = flipView;
4560
4561     /* Draw highlights */
4562     if (pm1X >= 0 && pm1Y >= 0) {
4563       drawHighlight(pm1X, pm1Y, prelineGC);
4564     }
4565     if (pm2X >= 0 && pm2Y >= 0) {
4566       drawHighlight(pm2X, pm2Y, prelineGC);
4567     }
4568     if (hi1X >= 0 && hi1Y >= 0) {
4569       drawHighlight(hi1X, hi1Y, highlineGC);
4570     }
4571     if (hi2X >= 0 && hi2Y >= 0) {
4572       drawHighlight(hi2X, hi2Y, highlineGC);
4573     }
4574   }
4575     /* If piece being dragged around board, must redraw that too */
4576     DrawDragPiece();
4577
4578     XSync(xDisplay, False);
4579 }
4580
4581
4582 /*
4583  * event handler for redrawing the board
4584  */
4585 void DrawPositionProc(w, event, prms, nprms)
4586      Widget w;
4587      XEvent *event;
4588      String *prms;
4589      Cardinal *nprms;
4590 {
4591     XDrawPosition(w, True, NULL);
4592 }
4593
4594
4595 /*
4596  * event handler for parsing user moves
4597  */
4598 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4599 //       way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4600 //       it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4601 //       should be made to use the new way, of calling UserMoveTest early  to determine the legality of the
4602 //       move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4603 //       and at the end FinishMove() to perform the move after optional promotion popups.
4604 //       For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4605 void HandleUserMove(w, event, prms, nprms)
4606      Widget w;
4607      XEvent *event;
4608      String *prms;
4609      Cardinal *nprms;
4610 {
4611     if (w != boardWidget || errorExitStatus != -1) return;
4612
4613     if (promotionUp) {
4614         if (event->type == ButtonPress) {
4615             XtPopdown(promotionShell);
4616             XtDestroyWidget(promotionShell);
4617             promotionUp = False;
4618             ClearHighlights();
4619             fromX = fromY = -1;
4620         } else {
4621             return;
4622         }
4623     }
4624
4625     // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4626     if(event->type == ButtonPress)   LeftClick(Press,   event->xbutton.x, event->xbutton.y);
4627     if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4628 }
4629
4630 void AnimateUserMove (Widget w, XEvent * event,
4631                       String * params, Cardinal * nParams)
4632 {
4633     DragPieceMove(event->xmotion.x, event->xmotion.y);
4634 }
4635
4636 void HandlePV (Widget w, XEvent * event,
4637                       String * params, Cardinal * nParams)
4638 {   // [HGM] pv: walk PV
4639     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4640 }
4641
4642 Widget CommentCreate(name, text, mutable, callback, lines)
4643      char *name, *text;
4644      int /*Boolean*/ mutable;
4645      XtCallbackProc callback;
4646      int lines;
4647 {
4648     Arg args[16];
4649     Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
4650     Dimension bw_width;
4651     int j;
4652
4653     j = 0;
4654     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
4655     XtGetValues(boardWidget, args, j);
4656
4657     j = 0;
4658     XtSetArg(args[j], XtNresizable, True);  j++;
4659 #if TOPLEVEL
4660     shell =
4661       XtCreatePopupShell(name, topLevelShellWidgetClass,
4662                          shellWidget, args, j);
4663 #else
4664     shell =
4665       XtCreatePopupShell(name, transientShellWidgetClass,
4666                          shellWidget, args, j);
4667 #endif
4668     layout =
4669       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4670                             layoutArgs, XtNumber(layoutArgs));
4671     form =
4672       XtCreateManagedWidget("form", formWidgetClass, layout,
4673                             formArgs, XtNumber(formArgs));
4674
4675     j = 0;
4676     if (mutable) {
4677         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
4678         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
4679     }
4680     XtSetArg(args[j], XtNstring, text);  j++;
4681     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
4682     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
4683     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
4684     XtSetArg(args[j], XtNright, XtChainRight);  j++;
4685     XtSetArg(args[j], XtNresizable, True);  j++;
4686     XtSetArg(args[j], XtNwidth, bw_width);  j++; /*force wider than buttons*/
4687     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4688     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
4689     XtSetArg(args[j], XtNautoFill, True);  j++;
4690     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4691     edit =
4692       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4693     XtOverrideTranslations(edit, XtParseTranslationTable(commentTranslations));
4694
4695     if (mutable) {
4696         j = 0;
4697         XtSetArg(args[j], XtNfromVert, edit);  j++;
4698         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4699         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4700         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4701         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4702         b_ok =
4703           XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
4704         XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
4705
4706         j = 0;
4707         XtSetArg(args[j], XtNfromVert, edit);  j++;
4708         XtSetArg(args[j], XtNfromHoriz, b_ok);  j++;
4709         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4710         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4711         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4712         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4713         b_cancel =
4714           XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
4715         XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
4716
4717         j = 0;
4718         XtSetArg(args[j], XtNfromVert, edit);  j++;
4719         XtSetArg(args[j], XtNfromHoriz, b_cancel);  j++;
4720         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4721         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4722         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4723         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4724         b_clear =
4725           XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
4726         XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
4727     } else {
4728         j = 0;
4729         XtSetArg(args[j], XtNfromVert, edit);  j++;
4730         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4731         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4732         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4733         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4734         b_close =
4735           XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
4736         XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
4737
4738         j = 0;
4739         XtSetArg(args[j], XtNfromVert, edit);  j++;
4740         XtSetArg(args[j], XtNfromHoriz, b_close);  j++;
4741         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4742         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4743         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4744         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4745         b_edit =
4746           XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
4747         XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
4748     }
4749
4750     XtRealizeWidget(shell);
4751
4752     if (commentX == -1) {
4753         int xx, yy;
4754         Window junk;
4755         Dimension pw_height;
4756         Dimension ew_height;
4757
4758         j = 0;
4759         XtSetArg(args[j], XtNheight, &ew_height);  j++;
4760         XtGetValues(edit, args, j);
4761
4762         j = 0;
4763         XtSetArg(args[j], XtNheight, &pw_height);  j++;
4764         XtGetValues(shell, args, j);
4765         commentH = pw_height + (lines - 1) * ew_height;
4766         commentW = bw_width - 16;
4767
4768         XSync(xDisplay, False);
4769 #ifdef NOTDEF
4770         /* This code seems to tickle an X bug if it is executed too soon
4771            after xboard starts up.  The coordinates get transformed as if
4772            the main window was positioned at (0, 0).
4773            */
4774         XtTranslateCoords(shellWidget,
4775                           (bw_width - commentW) / 2, 0 - commentH / 2,
4776                           &commentX, &commentY);
4777 #else  /*!NOTDEF*/
4778         XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4779                               RootWindowOfScreen(XtScreen(shellWidget)),
4780                               (bw_width - commentW) / 2, 0 - commentH / 2,
4781                               &xx, &yy, &junk);
4782         commentX = xx;
4783         commentY = yy;
4784 #endif /*!NOTDEF*/
4785         if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
4786     }
4787
4788     if(wpComment.width > 0) {
4789       commentX = wpComment.x;
4790       commentY = wpComment.y;
4791       commentW = wpComment.width;
4792       commentH = wpComment.height;
4793     }
4794
4795     j = 0;
4796     XtSetArg(args[j], XtNheight, commentH);  j++;
4797     XtSetArg(args[j], XtNwidth, commentW);  j++;
4798     XtSetArg(args[j], XtNx, commentX);  j++;
4799     XtSetArg(args[j], XtNy, commentY);  j++;
4800     XtSetValues(shell, args, j);
4801     XtSetKeyboardFocus(shell, edit);
4802
4803     return shell;
4804 }
4805
4806 /* Used for analysis window and ICS input window */
4807 Widget MiscCreate(name, text, mutable, callback, lines)
4808      char *name, *text;
4809      int /*Boolean*/ mutable;
4810      XtCallbackProc callback;
4811      int lines;
4812 {
4813     Arg args[16];
4814     Widget shell, layout, form, edit;
4815     Position x, y;
4816     Dimension bw_width, pw_height, ew_height, w, h;
4817     int j;
4818     int xx, yy;
4819     Window junk;
4820
4821     j = 0;
4822     XtSetArg(args[j], XtNresizable, True);  j++;
4823 #if TOPLEVEL
4824     shell =
4825       XtCreatePopupShell(name, topLevelShellWidgetClass,
4826                          shellWidget, args, j);
4827 #else
4828     shell =
4829       XtCreatePopupShell(name, transientShellWidgetClass,
4830                          shellWidget, args, j);
4831 #endif
4832     layout =
4833       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4834                             layoutArgs, XtNumber(layoutArgs));
4835     form =
4836       XtCreateManagedWidget("form", formWidgetClass, layout,
4837                             formArgs, XtNumber(formArgs));
4838
4839     j = 0;
4840     if (mutable) {
4841         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
4842         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
4843     }
4844     XtSetArg(args[j], XtNstring, text);  j++;
4845     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
4846     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
4847     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
4848     XtSetArg(args[j], XtNright, XtChainRight);  j++;
4849     XtSetArg(args[j], XtNresizable, True);  j++;
4850     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4851     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
4852     XtSetArg(args[j], XtNautoFill, True);  j++;
4853     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4854     edit =
4855       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4856
4857     XtRealizeWidget(shell);
4858
4859     j = 0;
4860     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
4861     XtGetValues(boardWidget, args, j);
4862
4863     j = 0;
4864     XtSetArg(args[j], XtNheight, &ew_height);  j++;
4865     XtGetValues(edit, args, j);
4866
4867     j = 0;
4868     XtSetArg(args[j], XtNheight, &pw_height);  j++;
4869     XtGetValues(shell, args, j);
4870     h = pw_height + (lines - 1) * ew_height;
4871     w = bw_width - 16;
4872
4873     XSync(xDisplay, False);
4874 #ifdef NOTDEF
4875     /* This code seems to tickle an X bug if it is executed too soon
4876        after xboard starts up.  The coordinates get transformed as if
4877        the main window was positioned at (0, 0).
4878     */
4879     XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
4880 #else  /*!NOTDEF*/
4881     XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4882                           RootWindowOfScreen(XtScreen(shellWidget)),
4883                           (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
4884 #endif /*!NOTDEF*/
4885     x = xx;
4886     y = yy;
4887     if (y < 0) y = 0; /*avoid positioning top offscreen*/
4888
4889     j = 0;
4890     XtSetArg(args[j], XtNheight, h);  j++;
4891     XtSetArg(args[j], XtNwidth, w);  j++;
4892     XtSetArg(args[j], XtNx, x);  j++;
4893     XtSetArg(args[j], XtNy, y);  j++;
4894     XtSetValues(shell, args, j);
4895
4896     return shell;
4897 }
4898
4899
4900 static int savedIndex;  /* gross that this is global */
4901
4902 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4903 {
4904         String val;
4905         XawTextPosition index, dummy;
4906         Arg arg;
4907
4908         XawTextGetSelectionPos(w, &index, &dummy);
4909         XtSetArg(arg, XtNstring, &val);
4910         XtGetValues(w, &arg, 1);
4911         ReplaceComment(savedIndex, val);
4912         if(savedIndex != currentMove) ToNrEvent(savedIndex);
4913         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4914 }
4915
4916 void EditCommentPopUp(index, title, text)
4917      int index;
4918      char *title, *text;
4919 {
4920     Widget edit;
4921     Arg args[16];
4922     int j;
4923
4924     savedIndex = index;
4925     if (text == NULL) text = "";
4926
4927     if (editShell == NULL) {
4928         editShell =
4929           CommentCreate(title, text, True, EditCommentCallback, 4);
4930         XtRealizeWidget(editShell);
4931         CatchDeleteWindow(editShell, "EditCommentPopDown");
4932     } else {
4933         edit = XtNameToWidget(editShell, "*form.text");
4934         j = 0;
4935         XtSetArg(args[j], XtNstring, text); j++;
4936         XtSetValues(edit, args, j);
4937         j = 0;
4938         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4939         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4940         XtSetValues(editShell, args, j);
4941     }
4942
4943     XtPopup(editShell, XtGrabNone);
4944
4945     editUp = True;
4946     j = 0;
4947     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
4948     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
4949                 args, j);
4950 }
4951
4952 void EditCommentCallback(w, client_data, call_data)
4953      Widget w;
4954      XtPointer client_data, call_data;
4955 {
4956     String name, val;
4957     Arg args[16];
4958     int j;
4959     Widget edit;
4960
4961     j = 0;
4962     XtSetArg(args[j], XtNlabel, &name);  j++;
4963     XtGetValues(w, args, j);
4964
4965     if (strcmp(name, _("ok")) == 0) {
4966         edit = XtNameToWidget(editShell, "*form.text");
4967         j = 0;
4968         XtSetArg(args[j], XtNstring, &val); j++;
4969         XtGetValues(edit, args, j);
4970         ReplaceComment(savedIndex, val);
4971         EditCommentPopDown();
4972     } else if (strcmp(name, _("cancel")) == 0) {
4973         EditCommentPopDown();
4974     } else if (strcmp(name, _("clear")) == 0) {
4975         edit = XtNameToWidget(editShell, "*form.text");
4976         XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4977         XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4978     }
4979 }
4980
4981 void EditCommentPopDown()
4982 {
4983     Arg args[16];
4984     int j;
4985
4986     if (!editUp) return;
4987     j = 0;
4988     XtSetArg(args[j], XtNx, &commentX); j++;
4989     XtSetArg(args[j], XtNy, &commentY); j++;
4990     XtSetArg(args[j], XtNheight, &commentH); j++;
4991     XtSetArg(args[j], XtNwidth, &commentW); j++;
4992     XtGetValues(editShell, args, j);
4993     XtPopdown(editShell);
4994     editUp = False;
4995     j = 0;
4996     XtSetArg(args[j], XtNleftBitmap, None); j++;
4997     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
4998                 args, j);
4999 }
5000
5001 void ICSInputBoxPopUp()
5002 {
5003     Widget edit;
5004     Arg args[16];
5005     int j;
5006     char *title = _("ICS Input");
5007     XtTranslations tr;
5008
5009     if (ICSInputShell == NULL) {
5010         ICSInputShell = MiscCreate(title, "", True, NULL, 1);
5011         tr = XtParseTranslationTable(ICSInputTranslations);
5012         edit = XtNameToWidget(ICSInputShell, "*form.text");
5013         XtOverrideTranslations(edit, tr);
5014         XtRealizeWidget(ICSInputShell);
5015         CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
5016
5017     } else {
5018         edit = XtNameToWidget(ICSInputShell, "*form.text");
5019         j = 0;
5020         XtSetArg(args[j], XtNstring, ""); j++;
5021         XtSetValues(edit, args, j);
5022         j = 0;
5023         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
5024         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
5025         XtSetValues(ICSInputShell, args, j);
5026     }
5027
5028     XtPopup(ICSInputShell, XtGrabNone);
5029     XtSetKeyboardFocus(ICSInputShell, edit);
5030
5031     ICSInputBoxUp = True;
5032     j = 0;
5033     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
5034     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
5035                 args, j);
5036 }
5037
5038 void ICSInputSendText()
5039 {
5040     Widget edit;
5041     int j;
5042     Arg args[16];
5043     String val;
5044
5045     edit = XtNameToWidget(ICSInputShell, "*form.text");
5046     j = 0;
5047     XtSetArg(args[j], XtNstring, &val); j++;
5048     XtGetValues(edit, args, j);
5049     SaveInHistory(val);
5050     SendMultiLineToICS(val);
5051     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5052     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5053 }
5054
5055 void ICSInputBoxPopDown()
5056 {
5057     Arg args[16];
5058     int j;
5059
5060     if (!ICSInputBoxUp) return;
5061     j = 0;
5062     XtPopdown(ICSInputShell);
5063     ICSInputBoxUp = False;
5064     j = 0;
5065     XtSetArg(args[j], XtNleftBitmap, None); j++;
5066     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
5067                 args, j);
5068 }
5069
5070 void CommentPopUp(title, text)
5071      char *title, *text;
5072 {
5073     Arg args[16];
5074     int j;
5075     Widget edit;
5076
5077     savedIndex = currentMove; // [HGM] vari
5078     if (commentShell == NULL) {
5079         commentShell =
5080           CommentCreate(title, text, False, CommentCallback, 4);
5081         XtRealizeWidget(commentShell);
5082         CatchDeleteWindow(commentShell, "CommentPopDown");
5083     } else {
5084         edit = XtNameToWidget(commentShell, "*form.text");
5085         j = 0;
5086         XtSetArg(args[j], XtNstring, text); j++;
5087         XtSetValues(edit, args, j);
5088         j = 0;
5089         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
5090         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
5091         XtSetValues(commentShell, args, j);
5092     }
5093
5094     XtPopup(commentShell, XtGrabNone);
5095     XSync(xDisplay, False);
5096
5097     commentUp = True;
5098 }
5099
5100 void CommentCallback(w, client_data, call_data)
5101      Widget w;
5102      XtPointer client_data, call_data;
5103 {
5104     String name;
5105     Arg args[16];
5106     int j;
5107
5108     j = 0;
5109     XtSetArg(args[j], XtNlabel, &name);  j++;
5110     XtGetValues(w, args, j);
5111
5112     if (strcmp(name, _("close")) == 0) {
5113         CommentPopDown();
5114     } else if (strcmp(name, _("edit")) == 0) {
5115         CommentPopDown();
5116         EditCommentEvent();
5117     }
5118 }
5119
5120
5121 void CommentPopDown()
5122 {
5123     Arg args[16];
5124     int j;
5125
5126     if (!commentUp) return;
5127     j = 0;
5128     XtSetArg(args[j], XtNx, &commentX); j++;
5129     XtSetArg(args[j], XtNy, &commentY); j++;
5130     XtSetArg(args[j], XtNwidth, &commentW); j++;
5131     XtSetArg(args[j], XtNheight, &commentH); j++;
5132     XtGetValues(commentShell, args, j);
5133     XtPopdown(commentShell);
5134     XSync(xDisplay, False);
5135     commentUp = False;
5136 }
5137
5138 void FileNamePopUp(label, def, proc, openMode)
5139      char *label;
5140      char *def;
5141      FileProc proc;
5142      char *openMode;
5143 {
5144     fileProc = proc;            /* I can't see a way not */
5145     fileOpenMode = openMode;    /*   to use globals here */
5146     {   // [HGM] use file-selector dialog stolen from Ghostview
5147         char *name;
5148         int index; // this is not supported yet
5149         FILE *f;
5150         if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
5151                            def, openMode, NULL, &name))
5152           (void) (*fileProc)(f, index=0, name);
5153     }
5154 }
5155
5156 void FileNamePopDown()
5157 {
5158     if (!filenameUp) return;
5159     XtPopdown(fileNameShell);
5160     XtDestroyWidget(fileNameShell);
5161     filenameUp = False;
5162     ModeHighlight();
5163 }
5164
5165 void FileNameCallback(w, client_data, call_data)
5166      Widget w;
5167      XtPointer client_data, call_data;
5168 {
5169     String name;
5170     Arg args[16];
5171
5172     XtSetArg(args[0], XtNlabel, &name);
5173     XtGetValues(w, args, 1);
5174
5175     if (strcmp(name, _("cancel")) == 0) {
5176         FileNamePopDown();
5177         return;
5178     }
5179
5180     FileNameAction(w, NULL, NULL, NULL);
5181 }
5182
5183 void FileNameAction(w, event, prms, nprms)
5184      Widget w;
5185      XEvent *event;
5186      String *prms;
5187      Cardinal *nprms;
5188 {
5189     char buf[MSG_SIZ];
5190     String name;
5191     FILE *f;
5192     char *p, *fullname;
5193     int index;
5194
5195     name = XawDialogGetValueString(w = XtParent(w));
5196
5197     if ((name != NULL) && (*name != NULLCHAR)) {
5198         safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5199         XtPopdown(w = XtParent(XtParent(w)));
5200         XtDestroyWidget(w);
5201         filenameUp = False;
5202
5203         p = strrchr(buf, ' ');
5204         if (p == NULL) {
5205             index = 0;
5206         } else {
5207             *p++ = NULLCHAR;
5208             index = atoi(p);
5209         }
5210         fullname = ExpandPathName(buf);
5211         if (!fullname) {
5212             ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5213         }
5214         else {
5215             f = fopen(fullname, fileOpenMode);
5216             if (f == NULL) {
5217                 DisplayError(_("Failed to open file"), errno);
5218             } else {
5219                 (void) (*fileProc)(f, index, buf);
5220             }
5221         }
5222         ModeHighlight();
5223         return;
5224     }
5225
5226     XtPopdown(w = XtParent(XtParent(w)));
5227     XtDestroyWidget(w);
5228     filenameUp = False;
5229     ModeHighlight();
5230 }
5231
5232 void PromotionPopUp()
5233 {
5234     Arg args[16];
5235     Widget dialog, layout;
5236     Position x, y;
5237     Dimension bw_width, pw_width;
5238     int j;
5239
5240     j = 0;
5241     XtSetArg(args[j], XtNwidth, &bw_width); j++;
5242     XtGetValues(boardWidget, args, j);
5243
5244     j = 0;
5245     XtSetArg(args[j], XtNresizable, True); j++;
5246     XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5247     promotionShell =
5248       XtCreatePopupShell("Promotion", transientShellWidgetClass,
5249                          shellWidget, args, j);
5250     layout =
5251       XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5252                             layoutArgs, XtNumber(layoutArgs));
5253
5254     j = 0;
5255     XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5256     XtSetArg(args[j], XtNborderWidth, 0); j++;
5257     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5258                                    layout, args, j);
5259
5260   if(gameInfo.variant != VariantShogi) {
5261     XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5262                        (XtPointer) dialog);
5263     XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5264                        (XtPointer) dialog);
5265     XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5266                        (XtPointer) dialog);
5267     XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5268                        (XtPointer) dialog);
5269     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5270         gameInfo.variant == VariantGiveaway) {
5271       XawDialogAddButton(dialog, _("King"), PromotionCallback,
5272                          (XtPointer) dialog);
5273     }
5274     if(gameInfo.variant == VariantCapablanca ||
5275        gameInfo.variant == VariantGothic ||
5276        gameInfo.variant == VariantCapaRandom) {
5277       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5278                          (XtPointer) dialog);
5279       XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5280                          (XtPointer) dialog);
5281     }
5282   } else // [HGM] shogi
5283   {
5284       XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5285                          (XtPointer) dialog);
5286       XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5287                          (XtPointer) dialog);
5288   }
5289     XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5290                        (XtPointer) dialog);
5291
5292     XtRealizeWidget(promotionShell);
5293     CatchDeleteWindow(promotionShell, "PromotionPopDown");
5294
5295     j = 0;
5296     XtSetArg(args[j], XtNwidth, &pw_width); j++;
5297     XtGetValues(promotionShell, args, j);
5298
5299     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5300                       lineGap + squareSize/3 +
5301                       ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5302                        0 : 6*(squareSize + lineGap)), &x, &y);
5303
5304     j = 0;
5305     XtSetArg(args[j], XtNx, x); j++;
5306     XtSetArg(args[j], XtNy, y); j++;
5307     XtSetValues(promotionShell, args, j);
5308
5309     XtPopup(promotionShell, XtGrabNone);
5310
5311     promotionUp = True;
5312 }
5313
5314 void PromotionPopDown()
5315 {
5316     if (!promotionUp) return;
5317     XtPopdown(promotionShell);
5318     XtDestroyWidget(promotionShell);
5319     promotionUp = False;
5320 }
5321
5322 void PromotionCallback(w, client_data, call_data)
5323      Widget w;
5324      XtPointer client_data, call_data;
5325 {
5326     String name;
5327     Arg args[16];
5328     int promoChar;
5329
5330     XtSetArg(args[0], XtNlabel, &name);
5331     XtGetValues(w, args, 1);
5332
5333     PromotionPopDown();
5334
5335     if (fromX == -1) return;
5336
5337     if (strcmp(name, _("cancel")) == 0) {
5338         fromX = fromY = -1;
5339         ClearHighlights();
5340         return;
5341     } else if (strcmp(name, _("Knight")) == 0) {
5342         promoChar = 'n';
5343     } else if (strcmp(name, _("Promote")) == 0) {
5344         promoChar = '+';
5345     } else if (strcmp(name, _("Defer")) == 0) {
5346         promoChar = '=';
5347     } else {
5348         promoChar = ToLower(name[0]);
5349     }
5350
5351     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5352
5353     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5354     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5355     fromX = fromY = -1;
5356 }
5357
5358
5359 void ErrorCallback(w, client_data, call_data)
5360      Widget w;
5361      XtPointer client_data, call_data;
5362 {
5363     errorUp = False;
5364     XtPopdown(w = XtParent(XtParent(XtParent(w))));
5365     XtDestroyWidget(w);
5366     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5367 }
5368
5369
5370 void ErrorPopDown()
5371 {
5372     if (!errorUp) return;
5373     errorUp = False;
5374     XtPopdown(errorShell);
5375     XtDestroyWidget(errorShell);
5376     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5377 }
5378
5379 void ErrorPopUp(title, label, modal)
5380      char *title, *label;
5381      int modal;
5382 {
5383     Arg args[16];
5384     Widget dialog, layout;
5385     Position x, y;
5386     int xx, yy;
5387     Window junk;
5388     Dimension bw_width, pw_width;
5389     Dimension pw_height;
5390     int i;
5391
5392     i = 0;
5393     XtSetArg(args[i], XtNresizable, True);  i++;
5394     XtSetArg(args[i], XtNtitle, title); i++;
5395     errorShell =
5396       XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5397                          shellWidget, args, i);
5398     layout =
5399       XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5400                             layoutArgs, XtNumber(layoutArgs));
5401
5402     i = 0;
5403     XtSetArg(args[i], XtNlabel, label); i++;
5404     XtSetArg(args[i], XtNborderWidth, 0); i++;
5405     dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5406                                    layout, args, i);
5407
5408     XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5409
5410     XtRealizeWidget(errorShell);
5411     CatchDeleteWindow(errorShell, "ErrorPopDown");
5412
5413     i = 0;
5414     XtSetArg(args[i], XtNwidth, &bw_width);  i++;
5415     XtGetValues(boardWidget, args, i);
5416     i = 0;
5417     XtSetArg(args[i], XtNwidth, &pw_width);  i++;
5418     XtSetArg(args[i], XtNheight, &pw_height);  i++;
5419     XtGetValues(errorShell, args, i);
5420
5421 #ifdef NOTDEF
5422     /* This code seems to tickle an X bug if it is executed too soon
5423        after xboard starts up.  The coordinates get transformed as if
5424        the main window was positioned at (0, 0).
5425        */
5426     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5427                       0 - pw_height + squareSize / 3, &x, &y);
5428 #else
5429     XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5430                           RootWindowOfScreen(XtScreen(boardWidget)),
5431                           (bw_width - pw_width) / 2,
5432                           0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5433     x = xx;
5434     y = yy;
5435 #endif
5436     if (y < 0) y = 0; /*avoid positioning top offscreen*/
5437
5438     i = 0;
5439     XtSetArg(args[i], XtNx, x);  i++;
5440     XtSetArg(args[i], XtNy, y);  i++;
5441     XtSetValues(errorShell, args, i);
5442
5443     errorUp = True;
5444     XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5445 }
5446
5447 /* Disable all user input other than deleting the window */
5448 static int frozen = 0;
5449 void FreezeUI()
5450 {
5451   if (frozen) return;
5452   /* Grab by a widget that doesn't accept input */
5453   XtAddGrab(messageWidget, TRUE, FALSE);
5454   frozen = 1;
5455 }
5456
5457 /* Undo a FreezeUI */
5458 void ThawUI()
5459 {
5460   if (!frozen) return;
5461   XtRemoveGrab(messageWidget);
5462   frozen = 0;
5463 }
5464
5465 char *ModeToWidgetName(mode)
5466      GameMode mode;
5467 {
5468     switch (mode) {
5469       case BeginningOfGame:
5470         if (appData.icsActive)
5471           return "menuMode.ICS Client";
5472         else if (appData.noChessProgram ||
5473                  *appData.cmailGameName != NULLCHAR)
5474           return "menuMode.Edit Game";
5475         else
5476           return "menuMode.Machine Black";
5477       case MachinePlaysBlack:
5478         return "menuMode.Machine Black";
5479       case MachinePlaysWhite:
5480         return "menuMode.Machine White";
5481       case AnalyzeMode:
5482         return "menuMode.Analysis Mode";
5483       case AnalyzeFile:
5484         return "menuMode.Analyze File";
5485       case TwoMachinesPlay:
5486         return "menuMode.Two Machines";
5487       case EditGame:
5488         return "menuMode.Edit Game";
5489       case PlayFromGameFile:
5490         return "menuFile.Load Game";
5491       case EditPosition:
5492         return "menuMode.Edit Position";
5493       case Training:
5494         return "menuMode.Training";
5495       case IcsPlayingWhite:
5496       case IcsPlayingBlack:
5497       case IcsObserving:
5498       case IcsIdle:
5499       case IcsExamining:
5500         return "menuMode.ICS Client";
5501       default:
5502       case EndOfGame:
5503         return NULL;
5504     }
5505 }
5506
5507 void ModeHighlight()
5508 {
5509     Arg args[16];
5510     static int oldPausing = FALSE;
5511     static GameMode oldmode = (GameMode) -1;
5512     char *wname;
5513
5514     if (!boardWidget || !XtIsRealized(boardWidget)) return;
5515
5516     if (pausing != oldPausing) {
5517         oldPausing = pausing;
5518         if (pausing) {
5519             XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5520         } else {
5521             XtSetArg(args[0], XtNleftBitmap, None);
5522         }
5523         XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5524                     args, 1);
5525
5526         if (appData.showButtonBar) {
5527           /* Always toggle, don't set.  Previous code messes up when
5528              invoked while the button is pressed, as releasing it
5529              toggles the state again. */
5530           {
5531             Pixel oldbg, oldfg;
5532             XtSetArg(args[0], XtNbackground, &oldbg);
5533             XtSetArg(args[1], XtNforeground, &oldfg);
5534             XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5535                         args, 2);
5536             XtSetArg(args[0], XtNbackground, oldfg);
5537             XtSetArg(args[1], XtNforeground, oldbg);
5538           }
5539           XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5540         }
5541     }
5542
5543     wname = ModeToWidgetName(oldmode);
5544     if (wname != NULL) {
5545         XtSetArg(args[0], XtNleftBitmap, None);
5546         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5547     }
5548     wname = ModeToWidgetName(gameMode);
5549     if (wname != NULL) {
5550         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5551         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5552     }
5553     oldmode = gameMode;
5554
5555     /* Maybe all the enables should be handled here, not just this one */
5556     XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5557                    gameMode == Training || gameMode == PlayFromGameFile);
5558 }
5559
5560
5561 /*
5562  * Button/menu procedures
5563  */
5564 void ResetProc(w, event, prms, nprms)
5565      Widget w;
5566      XEvent *event;
5567      String *prms;
5568      Cardinal *nprms;
5569 {
5570     ResetGameEvent();
5571 }
5572
5573 int LoadGamePopUp(f, gameNumber, title)
5574      FILE *f;
5575      int gameNumber;
5576      char *title;
5577 {
5578     cmailMsgLoaded = FALSE;
5579     if (gameNumber == 0) {
5580         int error = GameListBuild(f);
5581         if (error) {
5582             DisplayError(_("Cannot build game list"), error);
5583         } else if (!ListEmpty(&gameList) &&
5584                    ((ListGame *) gameList.tailPred)->number > 1) {
5585             GameListPopUp(f, title);
5586             return TRUE;
5587         }
5588         GameListDestroy();
5589         gameNumber = 1;
5590     }
5591     return LoadGame(f, gameNumber, title, FALSE);
5592 }
5593
5594 void LoadGameProc(w, event, prms, nprms)
5595      Widget w;
5596      XEvent *event;
5597      String *prms;
5598      Cardinal *nprms;
5599 {
5600     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5601         Reset(FALSE, TRUE);
5602     }
5603     FileNamePopUp(_("Load game file name?"), "", LoadGamePopUp, "rb");
5604 }
5605
5606 void LoadNextGameProc(w, event, prms, nprms)
5607      Widget w;
5608      XEvent *event;
5609      String *prms;
5610      Cardinal *nprms;
5611 {
5612     ReloadGame(1);
5613 }
5614
5615 void LoadPrevGameProc(w, event, prms, nprms)
5616      Widget w;
5617      XEvent *event;
5618      String *prms;
5619      Cardinal *nprms;
5620 {
5621     ReloadGame(-1);
5622 }
5623
5624 void ReloadGameProc(w, event, prms, nprms)
5625      Widget w;
5626      XEvent *event;
5627      String *prms;
5628      Cardinal *nprms;
5629 {
5630     ReloadGame(0);
5631 }
5632
5633 void LoadNextPositionProc(w, event, prms, nprms)
5634      Widget w;
5635      XEvent *event;
5636      String *prms;
5637      Cardinal *nprms;
5638 {
5639     ReloadPosition(1);
5640 }
5641
5642 void LoadPrevPositionProc(w, event, prms, nprms)
5643      Widget w;
5644      XEvent *event;
5645      String *prms;
5646      Cardinal *nprms;
5647 {
5648     ReloadPosition(-1);
5649 }
5650
5651 void ReloadPositionProc(w, event, prms, nprms)
5652      Widget w;
5653      XEvent *event;
5654      String *prms;
5655      Cardinal *nprms;
5656 {
5657     ReloadPosition(0);
5658 }
5659
5660 void LoadPositionProc(w, event, prms, nprms)
5661      Widget w;
5662      XEvent *event;
5663      String *prms;
5664      Cardinal *nprms;
5665 {
5666     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5667         Reset(FALSE, TRUE);
5668     }
5669     FileNamePopUp(_("Load position file name?"), "", LoadPosition, "rb");
5670 }
5671
5672 void SaveGameProc(w, event, prms, nprms)
5673      Widget w;
5674      XEvent *event;
5675      String *prms;
5676      Cardinal *nprms;
5677 {
5678     FileNamePopUp(_("Save game file name?"),
5679                   DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5680                   SaveGame, "a");
5681 }
5682
5683 void SavePositionProc(w, event, prms, nprms)
5684      Widget w;
5685      XEvent *event;
5686      String *prms;
5687      Cardinal *nprms;
5688 {
5689     FileNamePopUp(_("Save position file name?"),
5690                   DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5691                   SavePosition, "a");
5692 }
5693
5694 void ReloadCmailMsgProc(w, event, prms, nprms)
5695      Widget w;
5696      XEvent *event;
5697      String *prms;
5698      Cardinal *nprms;
5699 {
5700     ReloadCmailMsgEvent(FALSE);
5701 }
5702
5703 void MailMoveProc(w, event, prms, nprms)
5704      Widget w;
5705      XEvent *event;
5706      String *prms;
5707      Cardinal *nprms;
5708 {
5709     MailMoveEvent();
5710 }
5711
5712 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5713 char *selected_fen_position=NULL;
5714
5715 Boolean
5716 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5717                  Atom *type_return, XtPointer *value_return,
5718                  unsigned long *length_return, int *format_return)
5719 {
5720   char *selection_tmp;
5721
5722   if (!selected_fen_position) return False; /* should never happen */
5723   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5724     /* note: since no XtSelectionDoneProc was registered, Xt will
5725      * automatically call XtFree on the value returned.  So have to
5726      * make a copy of it allocated with XtMalloc */
5727     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5728     safeStrCpy(selection_tmp, selected_fen_position, sizeof(selection_tmp)/sizeof(selection_tmp[0]) );
5729
5730     *value_return=selection_tmp;
5731     *length_return=strlen(selection_tmp);
5732     *type_return=*target;
5733     *format_return = 8; /* bits per byte */
5734     return True;
5735   } else if (*target == XA_TARGETS(xDisplay)) {
5736     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5737     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5738     targets_tmp[1] = XA_STRING;
5739     *value_return = targets_tmp;
5740     *type_return = XA_ATOM;
5741     *length_return = 2;
5742     *format_return = 8 * sizeof(Atom);
5743     if (*format_return > 32) {
5744       *length_return *= *format_return / 32;
5745       *format_return = 32;
5746     }
5747     return True;
5748   } else {
5749     return False;
5750   }
5751 }
5752
5753 /* note: when called from menu all parameters are NULL, so no clue what the
5754  * Widget which was clicked on was, or what the click event was
5755  */
5756 void CopyPositionProc(w, event, prms, nprms)
5757   Widget w;
5758   XEvent *event;
5759   String *prms;
5760   Cardinal *nprms;
5761   {
5762     /*
5763      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5764      * have a notion of a position that is selected but not copied.
5765      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5766      */
5767     if(gameMode == EditPosition) EditPositionDone(TRUE);
5768     if (selected_fen_position) free(selected_fen_position);
5769     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5770     if (!selected_fen_position) return;
5771     XtOwnSelection(menuBarWidget, XA_PRIMARY,
5772                    CurrentTime,
5773                    SendPositionSelection,
5774                    NULL/* lose_ownership_proc */ ,
5775                    NULL/* transfer_done_proc */);
5776     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5777                    CurrentTime,
5778                    SendPositionSelection,
5779                    NULL/* lose_ownership_proc */ ,
5780                    NULL/* transfer_done_proc */);
5781   }
5782
5783 /* function called when the data to Paste is ready */
5784 static void
5785 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5786            Atom *type, XtPointer value, unsigned long *len, int *format)
5787 {
5788   char *fenstr=value;
5789   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5790   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5791   EditPositionPasteFEN(fenstr);
5792   XtFree(value);
5793 }
5794
5795 /* called when Paste Position button is pressed,
5796  * all parameters will be NULL */
5797 void PastePositionProc(w, event, prms, nprms)
5798   Widget w;
5799   XEvent *event;
5800   String *prms;
5801   Cardinal *nprms;
5802 {
5803     XtGetSelectionValue(menuBarWidget,
5804       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5805       /* (XtSelectionCallbackProc) */ PastePositionCB,
5806       NULL, /* client_data passed to PastePositionCB */
5807
5808       /* better to use the time field from the event that triggered the
5809        * call to this function, but that isn't trivial to get
5810        */
5811       CurrentTime
5812     );
5813     return;
5814 }
5815
5816 static Boolean
5817 SendGameSelection(Widget w, Atom *selection, Atom *target,
5818                   Atom *type_return, XtPointer *value_return,
5819                   unsigned long *length_return, int *format_return)
5820 {
5821   char *selection_tmp;
5822
5823   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5824     FILE* f = fopen(gameCopyFilename, "r");
5825     long len;
5826     size_t count;
5827     if (f == NULL) return False;
5828     fseek(f, 0, 2);
5829     len = ftell(f);
5830     rewind(f);
5831     selection_tmp = XtMalloc(len + 1);
5832     count = fread(selection_tmp, 1, len, f);
5833     if (len != count) {
5834       XtFree(selection_tmp);
5835       return False;
5836     }
5837     selection_tmp[len] = NULLCHAR;
5838     *value_return = selection_tmp;
5839     *length_return = len;
5840     *type_return = *target;
5841     *format_return = 8; /* bits per byte */
5842     return True;
5843   } else if (*target == XA_TARGETS(xDisplay)) {
5844     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5845     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5846     targets_tmp[1] = XA_STRING;
5847     *value_return = targets_tmp;
5848     *type_return = XA_ATOM;
5849     *length_return = 2;
5850     *format_return = 8 * sizeof(Atom);
5851     if (*format_return > 32) {
5852       *length_return *= *format_return / 32;
5853       *format_return = 32;
5854     }
5855     return True;
5856   } else {
5857     return False;
5858   }
5859 }
5860
5861 /* note: when called from menu all parameters are NULL, so no clue what the
5862  * Widget which was clicked on was, or what the click event was
5863  */
5864 void CopyGameProc(w, event, prms, nprms)
5865   Widget w;
5866   XEvent *event;
5867   String *prms;
5868   Cardinal *nprms;
5869 {
5870   int ret;
5871
5872   ret = SaveGameToFile(gameCopyFilename, FALSE);
5873   if (!ret) return;
5874
5875   /*
5876    * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5877    * have a notion of a game that is selected but not copied.
5878    * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5879    */
5880   XtOwnSelection(menuBarWidget, XA_PRIMARY,
5881                  CurrentTime,
5882                  SendGameSelection,
5883                  NULL/* lose_ownership_proc */ ,
5884                  NULL/* transfer_done_proc */);
5885   XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5886                  CurrentTime,
5887                  SendGameSelection,
5888                  NULL/* lose_ownership_proc */ ,
5889                  NULL/* transfer_done_proc */);
5890 }
5891
5892 /* function called when the data to Paste is ready */
5893 static void
5894 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5895             Atom *type, XtPointer value, unsigned long *len, int *format)
5896 {
5897   FILE* f;
5898   if (value == NULL || *len == 0) {
5899     return; /* nothing had been selected to copy */
5900   }
5901   f = fopen(gamePasteFilename, "w");
5902   if (f == NULL) {
5903     DisplayError(_("Can't open temp file"), errno);
5904     return;
5905   }
5906   fwrite(value, 1, *len, f);
5907   fclose(f);
5908   XtFree(value);
5909   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5910 }
5911
5912 /* called when Paste Game button is pressed,
5913  * all parameters will be NULL */
5914 void PasteGameProc(w, event, prms, nprms)
5915   Widget w;
5916   XEvent *event;
5917   String *prms;
5918   Cardinal *nprms;
5919 {
5920     XtGetSelectionValue(menuBarWidget,
5921       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5922       /* (XtSelectionCallbackProc) */ PasteGameCB,
5923       NULL, /* client_data passed to PasteGameCB */
5924
5925       /* better to use the time field from the event that triggered the
5926        * call to this function, but that isn't trivial to get
5927        */
5928       CurrentTime
5929     );
5930     return;
5931 }
5932
5933
5934 void AutoSaveGame()
5935 {
5936     SaveGameProc(NULL, NULL, NULL, NULL);
5937 }
5938
5939
5940 void QuitProc(w, event, prms, nprms)
5941      Widget w;
5942      XEvent *event;
5943      String *prms;
5944      Cardinal *nprms;
5945 {
5946     ExitEvent(0);
5947 }
5948
5949 void PauseProc(w, event, prms, nprms)
5950      Widget w;
5951      XEvent *event;
5952      String *prms;
5953      Cardinal *nprms;
5954 {
5955     PauseEvent();
5956 }
5957
5958
5959 void MachineBlackProc(w, event, prms, nprms)
5960      Widget w;
5961      XEvent *event;
5962      String *prms;
5963      Cardinal *nprms;
5964 {
5965     MachineBlackEvent();
5966 }
5967
5968 void MachineWhiteProc(w, event, prms, nprms)
5969      Widget w;
5970      XEvent *event;
5971      String *prms;
5972      Cardinal *nprms;
5973 {
5974     MachineWhiteEvent();
5975 }
5976
5977 void AnalyzeModeProc(w, event, prms, nprms)
5978      Widget w;
5979      XEvent *event;
5980      String *prms;
5981      Cardinal *nprms;
5982 {
5983     char buf[MSG_SIZ];
5984
5985     if (!first.analysisSupport) {
5986       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5987       DisplayError(buf, 0);
5988       return;
5989     }
5990     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5991     if (appData.icsActive) {
5992         if (gameMode != IcsObserving) {
5993           snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5994             DisplayError(buf, 0);
5995             /* secure check */
5996             if (appData.icsEngineAnalyze) {
5997                 if (appData.debugMode)
5998                     fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5999                 ExitAnalyzeMode();
6000                 ModeHighlight();
6001             }
6002             return;
6003         }
6004         /* if enable, use want disable icsEngineAnalyze */
6005         if (appData.icsEngineAnalyze) {
6006                 ExitAnalyzeMode();
6007                 ModeHighlight();
6008                 return;
6009         }
6010         appData.icsEngineAnalyze = TRUE;
6011         if (appData.debugMode)
6012             fprintf(debugFP, _("ICS engine analyze starting... \n"));
6013     }
6014     if (!appData.showThinking)
6015       ShowThinkingProc(w,event,prms,nprms);
6016
6017     AnalyzeModeEvent();
6018 }
6019
6020 void AnalyzeFileProc(w, event, prms, nprms)
6021      Widget w;
6022      XEvent *event;
6023      String *prms;
6024      Cardinal *nprms;
6025 {
6026     if (!first.analysisSupport) {
6027       char buf[MSG_SIZ];
6028       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6029       DisplayError(buf, 0);
6030       return;
6031     }
6032     Reset(FALSE, TRUE);
6033
6034     if (!appData.showThinking)
6035       ShowThinkingProc(w,event,prms,nprms);
6036
6037     AnalyzeFileEvent();
6038     FileNamePopUp(_("File to analyze"), "", LoadGamePopUp, "rb");
6039     AnalysisPeriodicEvent(1);
6040 }
6041
6042 void TwoMachinesProc(w, event, prms, nprms)
6043      Widget w;
6044      XEvent *event;
6045      String *prms;
6046      Cardinal *nprms;
6047 {
6048     TwoMachinesEvent();
6049 }
6050
6051 void IcsClientProc(w, event, prms, nprms)
6052      Widget w;
6053      XEvent *event;
6054      String *prms;
6055      Cardinal *nprms;
6056 {
6057     IcsClientEvent();
6058 }
6059
6060 void EditGameProc(w, event, prms, nprms)
6061      Widget w;
6062      XEvent *event;
6063      String *prms;
6064      Cardinal *nprms;
6065 {
6066     EditGameEvent();
6067 }
6068
6069 void EditPositionProc(w, event, prms, nprms)
6070      Widget w;
6071      XEvent *event;
6072      String *prms;
6073      Cardinal *nprms;
6074 {
6075     EditPositionEvent();
6076 }
6077
6078 void TrainingProc(w, event, prms, nprms)
6079      Widget w;
6080      XEvent *event;
6081      String *prms;
6082      Cardinal *nprms;
6083 {
6084     TrainingEvent();
6085 }
6086
6087 void EditCommentProc(w, event, prms, nprms)
6088      Widget w;
6089      XEvent *event;
6090      String *prms;
6091      Cardinal *nprms;
6092 {
6093     if (editUp) {
6094         EditCommentPopDown();
6095     } else {
6096         EditCommentEvent();
6097     }
6098 }
6099
6100 void IcsInputBoxProc(w, event, prms, nprms)
6101      Widget w;
6102      XEvent *event;
6103      String *prms;
6104      Cardinal *nprms;
6105 {
6106     if (ICSInputBoxUp) {
6107         ICSInputBoxPopDown();
6108     } else {
6109         ICSInputBoxPopUp();
6110     }
6111 }
6112
6113 void AcceptProc(w, event, prms, nprms)
6114      Widget w;
6115      XEvent *event;
6116      String *prms;
6117      Cardinal *nprms;
6118 {
6119     AcceptEvent();
6120 }
6121
6122 void DeclineProc(w, event, prms, nprms)
6123      Widget w;
6124      XEvent *event;
6125      String *prms;
6126      Cardinal *nprms;
6127 {
6128     DeclineEvent();
6129 }
6130
6131 void RematchProc(w, event, prms, nprms)
6132      Widget w;
6133      XEvent *event;
6134      String *prms;
6135      Cardinal *nprms;
6136 {
6137     RematchEvent();
6138 }
6139
6140 void CallFlagProc(w, event, prms, nprms)
6141      Widget w;
6142      XEvent *event;
6143      String *prms;
6144      Cardinal *nprms;
6145 {
6146     CallFlagEvent();
6147 }
6148
6149 void DrawProc(w, event, prms, nprms)
6150      Widget w;
6151      XEvent *event;
6152      String *prms;
6153      Cardinal *nprms;
6154 {
6155     DrawEvent();
6156 }
6157
6158 void AbortProc(w, event, prms, nprms)
6159      Widget w;
6160      XEvent *event;
6161      String *prms;
6162      Cardinal *nprms;
6163 {
6164     AbortEvent();
6165 }
6166
6167 void AdjournProc(w, event, prms, nprms)
6168      Widget w;
6169      XEvent *event;
6170      String *prms;
6171      Cardinal *nprms;
6172 {
6173     AdjournEvent();
6174 }
6175
6176 void ResignProc(w, event, prms, nprms)
6177      Widget w;
6178      XEvent *event;
6179      String *prms;
6180      Cardinal *nprms;
6181 {
6182     ResignEvent();
6183 }
6184
6185 void AdjuWhiteProc(w, event, prms, nprms)
6186      Widget w;
6187      XEvent *event;
6188      String *prms;
6189      Cardinal *nprms;
6190 {
6191     UserAdjudicationEvent(+1);
6192 }
6193
6194 void AdjuBlackProc(w, event, prms, nprms)
6195      Widget w;
6196      XEvent *event;
6197      String *prms;
6198      Cardinal *nprms;
6199 {
6200     UserAdjudicationEvent(-1);
6201 }
6202
6203 void AdjuDrawProc(w, event, prms, nprms)
6204      Widget w;
6205      XEvent *event;
6206      String *prms;
6207      Cardinal *nprms;
6208 {
6209     UserAdjudicationEvent(0);
6210 }
6211
6212 void EnterKeyProc(w, event, prms, nprms)
6213      Widget w;
6214      XEvent *event;
6215      String *prms;
6216      Cardinal *nprms;
6217 {
6218     if (ICSInputBoxUp == True)
6219       ICSInputSendText();
6220 }
6221
6222 void UpKeyProc(w, event, prms, nprms)
6223      Widget w;
6224      XEvent *event;
6225      String *prms;
6226      Cardinal *nprms;
6227 {   // [HGM] input: let up-arrow recall previous line from history
6228     Widget edit;
6229     int j;
6230     Arg args[16];
6231     String val;
6232     XawTextBlock t;
6233
6234     if (!ICSInputBoxUp) return;
6235     edit = XtNameToWidget(ICSInputShell, "*form.text");
6236     j = 0;
6237     XtSetArg(args[j], XtNstring, &val); j++;
6238     XtGetValues(edit, args, j);
6239     val = PrevInHistory(val);
6240     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6241     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6242     if(val) {
6243         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6244         XawTextReplace(edit, 0, 0, &t);
6245         XawTextSetInsertionPoint(edit, 9999);
6246     }
6247 }
6248
6249 void DownKeyProc(w, event, prms, nprms)
6250      Widget w;
6251      XEvent *event;
6252      String *prms;
6253      Cardinal *nprms;
6254 {   // [HGM] input: let down-arrow recall next line from history
6255     Widget edit;
6256     String val;
6257     XawTextBlock t;
6258
6259     if (!ICSInputBoxUp) return;
6260     edit = XtNameToWidget(ICSInputShell, "*form.text");
6261     val = NextInHistory();
6262     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6263     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6264     if(val) {
6265         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6266         XawTextReplace(edit, 0, 0, &t);
6267         XawTextSetInsertionPoint(edit, 9999);
6268     }
6269 }
6270
6271 void StopObservingProc(w, event, prms, nprms)
6272      Widget w;
6273      XEvent *event;
6274      String *prms;
6275      Cardinal *nprms;
6276 {
6277     StopObservingEvent();
6278 }
6279
6280 void StopExaminingProc(w, event, prms, nprms)
6281      Widget w;
6282      XEvent *event;
6283      String *prms;
6284      Cardinal *nprms;
6285 {
6286     StopExaminingEvent();
6287 }
6288
6289 void UploadProc(w, event, prms, nprms)
6290      Widget w;
6291      XEvent *event;
6292      String *prms;
6293      Cardinal *nprms;
6294 {
6295     UploadGameEvent();
6296 }
6297
6298
6299 void ForwardProc(w, event, prms, nprms)
6300      Widget w;
6301      XEvent *event;
6302      String *prms;
6303      Cardinal *nprms;
6304 {
6305     ForwardEvent();
6306 }
6307
6308
6309 void BackwardProc(w, event, prms, nprms)
6310      Widget w;
6311      XEvent *event;
6312      String *prms;
6313      Cardinal *nprms;
6314 {
6315     BackwardEvent();
6316 }
6317
6318 void ToStartProc(w, event, prms, nprms)
6319      Widget w;
6320      XEvent *event;
6321      String *prms;
6322      Cardinal *nprms;
6323 {
6324     ToStartEvent();
6325 }
6326
6327 void ToEndProc(w, event, prms, nprms)
6328      Widget w;
6329      XEvent *event;
6330      String *prms;
6331      Cardinal *nprms;
6332 {
6333     ToEndEvent();
6334 }
6335
6336 void RevertProc(w, event, prms, nprms)
6337      Widget w;
6338      XEvent *event;
6339      String *prms;
6340      Cardinal *nprms;
6341 {
6342     RevertEvent(False);
6343 }
6344
6345 void AnnotateProc(w, event, prms, nprms)
6346      Widget w;
6347      XEvent *event;
6348      String *prms;
6349      Cardinal *nprms;
6350 {
6351     RevertEvent(True);
6352 }
6353
6354 void TruncateGameProc(w, event, prms, nprms)
6355      Widget w;
6356      XEvent *event;
6357      String *prms;
6358      Cardinal *nprms;
6359 {
6360     TruncateGameEvent();
6361 }
6362 void RetractMoveProc(w, event, prms, nprms)
6363      Widget w;
6364      XEvent *event;
6365      String *prms;
6366      Cardinal *nprms;
6367 {
6368     RetractMoveEvent();
6369 }
6370
6371 void MoveNowProc(w, event, prms, nprms)
6372      Widget w;
6373      XEvent *event;
6374      String *prms;
6375      Cardinal *nprms;
6376 {
6377     MoveNowEvent();
6378 }
6379
6380
6381 void AlwaysQueenProc(w, event, prms, nprms)
6382      Widget w;
6383      XEvent *event;
6384      String *prms;
6385      Cardinal *nprms;
6386 {
6387     Arg args[16];
6388
6389     appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6390
6391     if (appData.alwaysPromoteToQueen) {
6392         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6393     } else {
6394         XtSetArg(args[0], XtNleftBitmap, None);
6395     }
6396     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6397                 args, 1);
6398 }
6399
6400 void AnimateDraggingProc(w, event, prms, nprms)
6401      Widget w;
6402      XEvent *event;
6403      String *prms;
6404      Cardinal *nprms;
6405 {
6406     Arg args[16];
6407
6408     appData.animateDragging = !appData.animateDragging;
6409
6410     if (appData.animateDragging) {
6411         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6412         CreateAnimVars();
6413     } else {
6414         XtSetArg(args[0], XtNleftBitmap, None);
6415     }
6416     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6417                 args, 1);
6418 }
6419
6420 void AnimateMovingProc(w, event, prms, nprms)
6421      Widget w;
6422      XEvent *event;
6423      String *prms;
6424      Cardinal *nprms;
6425 {
6426     Arg args[16];
6427
6428     appData.animate = !appData.animate;
6429
6430     if (appData.animate) {
6431         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6432         CreateAnimVars();
6433     } else {
6434         XtSetArg(args[0], XtNleftBitmap, None);
6435     }
6436     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6437                 args, 1);
6438 }
6439
6440 void AutocommProc(w, event, prms, nprms)
6441      Widget w;
6442      XEvent *event;
6443      String *prms;
6444      Cardinal *nprms;
6445 {
6446     Arg args[16];
6447
6448     appData.autoComment = !appData.autoComment;
6449
6450     if (appData.autoComment) {
6451         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6452     } else {
6453         XtSetArg(args[0], XtNleftBitmap, None);
6454     }
6455     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
6456                 args, 1);
6457 }
6458
6459
6460 void AutoflagProc(w, event, prms, nprms)
6461      Widget w;
6462      XEvent *event;
6463      String *prms;
6464      Cardinal *nprms;
6465 {
6466     Arg args[16];
6467
6468     appData.autoCallFlag = !appData.autoCallFlag;
6469
6470     if (appData.autoCallFlag) {
6471         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6472     } else {
6473         XtSetArg(args[0], XtNleftBitmap, None);
6474     }
6475     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6476                 args, 1);
6477 }
6478
6479 void AutoflipProc(w, event, prms, nprms)
6480      Widget w;
6481      XEvent *event;
6482      String *prms;
6483      Cardinal *nprms;
6484 {
6485     Arg args[16];
6486
6487     appData.autoFlipView = !appData.autoFlipView;
6488
6489     if (appData.autoFlipView) {
6490         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6491     } else {
6492         XtSetArg(args[0], XtNleftBitmap, None);
6493     }
6494     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6495                 args, 1);
6496 }
6497
6498 void AutobsProc(w, event, prms, nprms)
6499      Widget w;
6500      XEvent *event;
6501      String *prms;
6502      Cardinal *nprms;
6503 {
6504     Arg args[16];
6505
6506     appData.autoObserve = !appData.autoObserve;
6507
6508     if (appData.autoObserve) {
6509         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6510     } else {
6511         XtSetArg(args[0], XtNleftBitmap, None);
6512     }
6513     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
6514                 args, 1);
6515 }
6516
6517 void AutoraiseProc(w, event, prms, nprms)
6518      Widget w;
6519      XEvent *event;
6520      String *prms;
6521      Cardinal *nprms;
6522 {
6523     Arg args[16];
6524
6525     appData.autoRaiseBoard = !appData.autoRaiseBoard;
6526
6527     if (appData.autoRaiseBoard) {
6528         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6529     } else {
6530         XtSetArg(args[0], XtNleftBitmap, None);
6531     }
6532     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Raise Board"),
6533                 args, 1);
6534 }
6535
6536 void AutosaveProc(w, event, prms, nprms)
6537      Widget w;
6538      XEvent *event;
6539      String *prms;
6540      Cardinal *nprms;
6541 {
6542     Arg args[16];
6543
6544     appData.autoSaveGames = !appData.autoSaveGames;
6545
6546     if (appData.autoSaveGames) {
6547         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6548     } else {
6549         XtSetArg(args[0], XtNleftBitmap, None);
6550     }
6551     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
6552                 args, 1);
6553 }
6554
6555 void BlindfoldProc(w, event, prms, nprms)
6556      Widget w;
6557      XEvent *event;
6558      String *prms;
6559      Cardinal *nprms;
6560 {
6561     Arg args[16];
6562
6563     appData.blindfold = !appData.blindfold;
6564
6565     if (appData.blindfold) {
6566         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6567     } else {
6568         XtSetArg(args[0], XtNleftBitmap, None);
6569     }
6570     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6571                 args, 1);
6572
6573     DrawPosition(True, NULL);
6574 }
6575
6576 void TestLegalityProc(w, event, prms, nprms)
6577      Widget w;
6578      XEvent *event;
6579      String *prms;
6580      Cardinal *nprms;
6581 {
6582     Arg args[16];
6583
6584     appData.testLegality = !appData.testLegality;
6585
6586     if (appData.testLegality) {
6587         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6588     } else {
6589         XtSetArg(args[0], XtNleftBitmap, None);
6590     }
6591     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6592                 args, 1);
6593 }
6594
6595
6596 void FlashMovesProc(w, event, prms, nprms)
6597      Widget w;
6598      XEvent *event;
6599      String *prms;
6600      Cardinal *nprms;
6601 {
6602     Arg args[16];
6603
6604     if (appData.flashCount == 0) {
6605         appData.flashCount = 3;
6606     } else {
6607         appData.flashCount = -appData.flashCount;
6608     }
6609
6610     if (appData.flashCount > 0) {
6611         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6612     } else {
6613         XtSetArg(args[0], XtNleftBitmap, None);
6614     }
6615     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6616                 args, 1);
6617 }
6618
6619 void FlipViewProc(w, event, prms, nprms)
6620      Widget w;
6621      XEvent *event;
6622      String *prms;
6623      Cardinal *nprms;
6624 {
6625     flipView = !flipView;
6626     DrawPosition(True, NULL);
6627 }
6628
6629 void GetMoveListProc(w, event, prms, nprms)
6630      Widget w;
6631      XEvent *event;
6632      String *prms;
6633      Cardinal *nprms;
6634 {
6635     Arg args[16];
6636
6637     appData.getMoveList = !appData.getMoveList;
6638
6639     if (appData.getMoveList) {
6640         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6641         GetMoveListEvent();
6642     } else {
6643         XtSetArg(args[0], XtNleftBitmap, None);
6644     }
6645     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
6646                 args, 1);
6647 }
6648
6649 #if HIGHDRAG
6650 void HighlightDraggingProc(w, event, prms, nprms)
6651      Widget w;
6652      XEvent *event;
6653      String *prms;
6654      Cardinal *nprms;
6655 {
6656     Arg args[16];
6657
6658     appData.highlightDragging = !appData.highlightDragging;
6659
6660     if (appData.highlightDragging) {
6661         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6662     } else {
6663         XtSetArg(args[0], XtNleftBitmap, None);
6664     }
6665     XtSetValues(XtNameToWidget(menuBarWidget,
6666                                "menuOptions.Highlight Dragging"), args, 1);
6667 }
6668 #endif
6669
6670 void HighlightLastMoveProc(w, event, prms, nprms)
6671      Widget w;
6672      XEvent *event;
6673      String *prms;
6674      Cardinal *nprms;
6675 {
6676     Arg args[16];
6677
6678     appData.highlightLastMove = !appData.highlightLastMove;
6679
6680     if (appData.highlightLastMove) {
6681         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6682     } else {
6683         XtSetArg(args[0], XtNleftBitmap, None);
6684     }
6685     XtSetValues(XtNameToWidget(menuBarWidget,
6686                                "menuOptions.Highlight Last Move"), args, 1);
6687 }
6688
6689 void IcsAlarmProc(w, event, prms, nprms)
6690      Widget w;
6691      XEvent *event;
6692      String *prms;
6693      Cardinal *nprms;
6694 {
6695     Arg args[16];
6696
6697     appData.icsAlarm = !appData.icsAlarm;
6698
6699     if (appData.icsAlarm) {
6700         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6701     } else {
6702         XtSetArg(args[0], XtNleftBitmap, None);
6703     }
6704     XtSetValues(XtNameToWidget(menuBarWidget,
6705                                "menuOptions.ICS Alarm"), args, 1);
6706 }
6707
6708 void MoveSoundProc(w, event, prms, nprms)
6709      Widget w;
6710      XEvent *event;
6711      String *prms;
6712      Cardinal *nprms;
6713 {
6714     Arg args[16];
6715
6716     appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6717
6718     if (appData.ringBellAfterMoves) {
6719         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6720     } else {
6721         XtSetArg(args[0], XtNleftBitmap, None);
6722     }
6723     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6724                 args, 1);
6725 }
6726
6727
6728 void OldSaveStyleProc(w, event, prms, nprms)
6729      Widget w;
6730      XEvent *event;
6731      String *prms;
6732      Cardinal *nprms;
6733 {
6734     Arg args[16];
6735
6736     appData.oldSaveStyle = !appData.oldSaveStyle;
6737
6738     if (appData.oldSaveStyle) {
6739         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6740     } else {
6741         XtSetArg(args[0], XtNleftBitmap, None);
6742     }
6743     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Old Save Style"),
6744                 args, 1);
6745 }
6746
6747 void PeriodicUpdatesProc(w, event, prms, nprms)
6748      Widget w;
6749      XEvent *event;
6750      String *prms;
6751      Cardinal *nprms;
6752 {
6753     Arg args[16];
6754
6755     PeriodicUpdatesEvent(!appData.periodicUpdates);
6756
6757     if (appData.periodicUpdates) {
6758         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6759     } else {
6760         XtSetArg(args[0], XtNleftBitmap, None);
6761     }
6762     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6763                 args, 1);
6764 }
6765
6766 void PonderNextMoveProc(w, event, prms, nprms)
6767      Widget w;
6768      XEvent *event;
6769      String *prms;
6770      Cardinal *nprms;
6771 {
6772     Arg args[16];
6773
6774     PonderNextMoveEvent(!appData.ponderNextMove);
6775
6776     if (appData.ponderNextMove) {
6777         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6778     } else {
6779         XtSetArg(args[0], XtNleftBitmap, None);
6780     }
6781     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6782                 args, 1);
6783 }
6784
6785 void PopupExitMessageProc(w, event, prms, nprms)
6786      Widget w;
6787      XEvent *event;
6788      String *prms;
6789      Cardinal *nprms;
6790 {
6791     Arg args[16];
6792
6793     appData.popupExitMessage = !appData.popupExitMessage;
6794
6795     if (appData.popupExitMessage) {
6796         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6797     } else {
6798         XtSetArg(args[0], XtNleftBitmap, None);
6799     }
6800     XtSetValues(XtNameToWidget(menuBarWidget,
6801                                "menuOptions.Popup Exit Message"), args, 1);
6802 }
6803
6804 void PopupMoveErrorsProc(w, event, prms, nprms)
6805      Widget w;
6806      XEvent *event;
6807      String *prms;
6808      Cardinal *nprms;
6809 {
6810     Arg args[16];
6811
6812     appData.popupMoveErrors = !appData.popupMoveErrors;
6813
6814     if (appData.popupMoveErrors) {
6815         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6816     } else {
6817         XtSetArg(args[0], XtNleftBitmap, None);
6818     }
6819     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6820                 args, 1);
6821 }
6822
6823 void PremoveProc(w, event, prms, nprms)
6824      Widget w;
6825      XEvent *event;
6826      String *prms;
6827      Cardinal *nprms;
6828 {
6829     Arg args[16];
6830
6831     appData.premove = !appData.premove;
6832
6833     if (appData.premove) {
6834         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6835     } else {
6836         XtSetArg(args[0], XtNleftBitmap, None);
6837     }
6838     XtSetValues(XtNameToWidget(menuBarWidget,
6839                                "menuOptions.Premove"), args, 1);
6840 }
6841
6842 void QuietPlayProc(w, event, prms, nprms)
6843      Widget w;
6844      XEvent *event;
6845      String *prms;
6846      Cardinal *nprms;
6847 {
6848     Arg args[16];
6849
6850     appData.quietPlay = !appData.quietPlay;
6851
6852     if (appData.quietPlay) {
6853         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6854     } else {
6855         XtSetArg(args[0], XtNleftBitmap, None);
6856     }
6857     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Quiet Play"),
6858                 args, 1);
6859 }
6860
6861 void ShowCoordsProc(w, event, prms, nprms)
6862      Widget w;
6863      XEvent *event;
6864      String *prms;
6865      Cardinal *nprms;
6866 {
6867     Arg args[16];
6868
6869     appData.showCoords = !appData.showCoords;
6870
6871     if (appData.showCoords) {
6872         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6873     } else {
6874         XtSetArg(args[0], XtNleftBitmap, None);
6875     }
6876     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6877                 args, 1);
6878
6879     DrawPosition(True, NULL);
6880 }
6881
6882 void ShowThinkingProc(w, event, prms, nprms)
6883      Widget w;
6884      XEvent *event;
6885      String *prms;
6886      Cardinal *nprms;
6887 {
6888     appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6889     ShowThinkingEvent();
6890 }
6891
6892 void HideThinkingProc(w, event, prms, nprms)
6893      Widget w;
6894      XEvent *event;
6895      String *prms;
6896      Cardinal *nprms;
6897 {
6898     Arg args[16];
6899
6900     appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6901     ShowThinkingEvent();
6902
6903     if (appData.hideThinkingFromHuman) {
6904         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6905     } else {
6906         XtSetArg(args[0], XtNleftBitmap, None);
6907     }
6908     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6909                 args, 1);
6910 }
6911
6912 void SaveOnExitProc(w, event, prms, nprms)
6913      Widget w;
6914      XEvent *event;
6915      String *prms;
6916      Cardinal *nprms;
6917 {
6918     Arg args[16];
6919
6920     saveSettingsOnExit = !saveSettingsOnExit;
6921
6922     if (saveSettingsOnExit) {
6923         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6924     } else {
6925         XtSetArg(args[0], XtNleftBitmap, None);
6926     }
6927     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6928                 args, 1);
6929 }
6930
6931 void SaveSettingsProc(w, event, prms, nprms)
6932      Widget w;
6933      XEvent *event;
6934      String *prms;
6935      Cardinal *nprms;
6936 {
6937      SaveSettings(settingsFileName);
6938 }
6939
6940 void InfoProc(w, event, prms, nprms)
6941      Widget w;
6942      XEvent *event;
6943      String *prms;
6944      Cardinal *nprms;
6945 {
6946     char buf[MSG_SIZ];
6947     snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6948             INFODIR, INFOFILE);
6949     system(buf);
6950 }
6951
6952 void ManProc(w, event, prms, nprms)
6953      Widget w;
6954      XEvent *event;
6955      String *prms;
6956      Cardinal *nprms;
6957 {
6958     char buf[MSG_SIZ];
6959     String name;
6960     if (nprms && *nprms > 0)
6961       name = prms[0];
6962     else
6963       name = "xboard";
6964     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6965     system(buf);
6966 }
6967
6968 void HintProc(w, event, prms, nprms)
6969      Widget w;
6970      XEvent *event;
6971      String *prms;
6972      Cardinal *nprms;
6973 {
6974     HintEvent();
6975 }
6976
6977 void BookProc(w, event, prms, nprms)
6978      Widget w;
6979      XEvent *event;
6980      String *prms;
6981      Cardinal *nprms;
6982 {
6983     BookEvent();
6984 }
6985
6986 void AboutProc(w, event, prms, nprms)
6987      Widget w;
6988      XEvent *event;
6989      String *prms;
6990      Cardinal *nprms;
6991 {
6992     char buf[MSG_SIZ];
6993 #if ZIPPY
6994     char *zippy = " (with Zippy code)";
6995 #else
6996     char *zippy = "";
6997 #endif
6998     snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6999             programVersion, zippy,
7000             "Copyright 1991 Digital Equipment Corporation",
7001             "Enhancements Copyright 1992-2009 Free Software Foundation",
7002             "Enhancements Copyright 2005 Alessandro Scotti",
7003             PACKAGE, " is free software and carries NO WARRANTY;",
7004             "see the file COPYING for more information.");
7005     ErrorPopUp(_("About XBoard"), buf, FALSE);
7006 }
7007
7008 void DebugProc(w, event, prms, nprms)
7009      Widget w;
7010      XEvent *event;
7011      String *prms;
7012      Cardinal *nprms;
7013 {
7014     appData.debugMode = !appData.debugMode;
7015 }
7016
7017 void AboutGameProc(w, event, prms, nprms)
7018      Widget w;
7019      XEvent *event;
7020      String *prms;
7021      Cardinal *nprms;
7022 {
7023     AboutGameEvent();
7024 }
7025
7026 void NothingProc(w, event, prms, nprms)
7027      Widget w;
7028      XEvent *event;
7029      String *prms;
7030      Cardinal *nprms;
7031 {
7032     return;
7033 }
7034
7035 void Iconify(w, event, prms, nprms)
7036      Widget w;
7037      XEvent *event;
7038      String *prms;
7039      Cardinal *nprms;
7040 {
7041     Arg args[16];
7042
7043     fromX = fromY = -1;
7044     XtSetArg(args[0], XtNiconic, True);
7045     XtSetValues(shellWidget, args, 1);
7046 }
7047
7048 void DisplayMessage(message, extMessage)
7049      char *message, *extMessage;
7050 {
7051   /* display a message in the message widget */
7052
7053   char buf[MSG_SIZ];
7054   Arg arg;
7055
7056   if (extMessage)
7057     {
7058       if (*message)
7059         {
7060           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
7061           message = buf;
7062         }
7063       else
7064         {
7065           message = extMessage;
7066         };
7067     };
7068
7069   /* need to test if messageWidget already exists, since this function
7070      can also be called during the startup, if for example a Xresource
7071      is not set up correctly */
7072   if(messageWidget)
7073     {
7074       XtSetArg(arg, XtNlabel, message);
7075       XtSetValues(messageWidget, &arg, 1);
7076     };
7077
7078   return;
7079 }
7080
7081 void DisplayTitle(text)
7082      char *text;
7083 {
7084     Arg args[16];
7085     int i;
7086     char title[MSG_SIZ];
7087     char icon[MSG_SIZ];
7088
7089     if (text == NULL) text = "";
7090
7091     if (appData.titleInWindow) {
7092         i = 0;
7093         XtSetArg(args[i], XtNlabel, text);   i++;
7094         XtSetValues(titleWidget, args, i);
7095     }
7096
7097     if (*text != NULLCHAR) {
7098       safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
7099       safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
7100     } else if (appData.icsActive) {
7101         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
7102         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
7103     } else if (appData.cmailGameName[0] != NULLCHAR) {
7104         snprintf(icon, sizeof(icon), "%s", "CMail");
7105         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
7106 #ifdef GOTHIC
7107     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
7108     } else if (gameInfo.variant == VariantGothic) {
7109       safeStrCpy(icon,  programName, sizeof(icon)/sizeof(icon[0]) );
7110       safeStrCpy(title, GOTHIC,     sizeof(title)/sizeof(title[0]) );
7111 #endif
7112 #ifdef FALCON
7113     } else if (gameInfo.variant == VariantFalcon) {
7114       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7115       safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
7116 #endif
7117     } else if (appData.noChessProgram) {
7118       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7119       safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
7120     } else {
7121       safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
7122         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
7123     }
7124     i = 0;
7125     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
7126     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
7127     XtSetValues(shellWidget, args, i);
7128 }
7129
7130
7131 void
7132 DisplayError(message, error)
7133      String message;
7134      int error;
7135 {
7136     char buf[MSG_SIZ];
7137
7138     if (error == 0) {
7139         if (appData.debugMode || appData.matchMode) {
7140             fprintf(stderr, "%s: %s\n", programName, message);
7141         }
7142     } else {
7143         if (appData.debugMode || appData.matchMode) {
7144             fprintf(stderr, "%s: %s: %s\n",
7145                     programName, message, strerror(error));
7146         }
7147         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7148         message = buf;
7149     }
7150     ErrorPopUp(_("Error"), message, FALSE);
7151 }
7152
7153
7154 void DisplayMoveError(message)
7155      String message;
7156 {
7157     fromX = fromY = -1;
7158     ClearHighlights();
7159     DrawPosition(FALSE, NULL);
7160     if (appData.debugMode || appData.matchMode) {
7161         fprintf(stderr, "%s: %s\n", programName, message);
7162     }
7163     if (appData.popupMoveErrors) {
7164         ErrorPopUp(_("Error"), message, FALSE);
7165     } else {
7166         DisplayMessage(message, "");
7167     }
7168 }
7169
7170
7171 void DisplayFatalError(message, error, status)
7172      String message;
7173      int error, status;
7174 {
7175     char buf[MSG_SIZ];
7176
7177     errorExitStatus = status;
7178     if (error == 0) {
7179         fprintf(stderr, "%s: %s\n", programName, message);
7180     } else {
7181         fprintf(stderr, "%s: %s: %s\n",
7182                 programName, message, strerror(error));
7183         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7184         message = buf;
7185     }
7186     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7187       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7188     } else {
7189       ExitEvent(status);
7190     }
7191 }
7192
7193 void DisplayInformation(message)
7194      String message;
7195 {
7196     ErrorPopDown();
7197     ErrorPopUp(_("Information"), message, TRUE);
7198 }
7199
7200 void DisplayNote(message)
7201      String message;
7202 {
7203     ErrorPopDown();
7204     ErrorPopUp(_("Note"), message, FALSE);
7205 }
7206
7207 static int
7208 NullXErrorCheck(dpy, error_event)
7209      Display *dpy;
7210      XErrorEvent *error_event;
7211 {
7212     return 0;
7213 }
7214
7215 void DisplayIcsInteractionTitle(message)
7216      String message;
7217 {
7218   if (oldICSInteractionTitle == NULL) {
7219     /* Magic to find the old window title, adapted from vim */
7220     char *wina = getenv("WINDOWID");
7221     if (wina != NULL) {
7222       Window win = (Window) atoi(wina);
7223       Window root, parent, *children;
7224       unsigned int nchildren;
7225       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7226       for (;;) {
7227         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7228         if (!XQueryTree(xDisplay, win, &root, &parent,
7229                         &children, &nchildren)) break;
7230         if (children) XFree((void *)children);
7231         if (parent == root || parent == 0) break;
7232         win = parent;
7233       }
7234       XSetErrorHandler(oldHandler);
7235     }
7236     if (oldICSInteractionTitle == NULL) {
7237       oldICSInteractionTitle = "xterm";
7238     }
7239   }
7240   printf("\033]0;%s\007", message);
7241   fflush(stdout);
7242 }
7243
7244 char pendingReplyPrefix[MSG_SIZ];
7245 ProcRef pendingReplyPR;
7246
7247 void AskQuestionProc(w, event, prms, nprms)
7248      Widget w;
7249      XEvent *event;
7250      String *prms;
7251      Cardinal *nprms;
7252 {
7253     if (*nprms != 4) {
7254         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7255                 *nprms);
7256         return;
7257     }
7258     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7259 }
7260
7261 void AskQuestionPopDown()
7262 {
7263     if (!askQuestionUp) return;
7264     XtPopdown(askQuestionShell);
7265     XtDestroyWidget(askQuestionShell);
7266     askQuestionUp = False;
7267 }
7268
7269 void AskQuestionReplyAction(w, event, prms, nprms)
7270      Widget w;
7271      XEvent *event;
7272      String *prms;
7273      Cardinal *nprms;
7274 {
7275     char buf[MSG_SIZ];
7276     int err;
7277     String reply;
7278
7279     reply = XawDialogGetValueString(w = XtParent(w));
7280     safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7281     if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7282     strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7283     strncat(buf, "\n",  MSG_SIZ - strlen(buf) - 1);
7284     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7285     AskQuestionPopDown();
7286
7287     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7288 }
7289
7290 void AskQuestionCallback(w, client_data, call_data)
7291      Widget w;
7292      XtPointer client_data, call_data;
7293 {
7294     String name;
7295     Arg args[16];
7296
7297     XtSetArg(args[0], XtNlabel, &name);
7298     XtGetValues(w, args, 1);
7299
7300     if (strcmp(name, _("cancel")) == 0) {
7301         AskQuestionPopDown();
7302     } else {
7303         AskQuestionReplyAction(w, NULL, NULL, NULL);
7304     }
7305 }
7306
7307 void AskQuestion(title, question, replyPrefix, pr)
7308      char *title, *question, *replyPrefix;
7309      ProcRef pr;
7310 {
7311     Arg args[16];
7312     Widget popup, layout, dialog, edit;
7313     Window root, child;
7314     int x, y, i;
7315     int win_x, win_y;
7316     unsigned int mask;
7317
7318     safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7319     pendingReplyPR = pr;
7320
7321     i = 0;
7322     XtSetArg(args[i], XtNresizable, True); i++;
7323     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7324     askQuestionShell = popup =
7325       XtCreatePopupShell(title, transientShellWidgetClass,
7326                          shellWidget, args, i);
7327
7328     layout =
7329       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7330                             layoutArgs, XtNumber(layoutArgs));
7331
7332     i = 0;
7333     XtSetArg(args[i], XtNlabel, question); i++;
7334     XtSetArg(args[i], XtNvalue, ""); i++;
7335     XtSetArg(args[i], XtNborderWidth, 0); i++;
7336     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7337                                    layout, args, i);
7338
7339     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7340                        (XtPointer) dialog);
7341     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7342                        (XtPointer) dialog);
7343
7344     XtRealizeWidget(popup);
7345     CatchDeleteWindow(popup, "AskQuestionPopDown");
7346
7347     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7348                   &x, &y, &win_x, &win_y, &mask);
7349
7350     XtSetArg(args[0], XtNx, x - 10);
7351     XtSetArg(args[1], XtNy, y - 30);
7352     XtSetValues(popup, args, 2);
7353
7354     XtPopup(popup, XtGrabExclusive);
7355     askQuestionUp = True;
7356
7357     edit = XtNameToWidget(dialog, "*value");
7358     XtSetKeyboardFocus(popup, edit);
7359 }
7360
7361
7362 void
7363 PlaySound(name)
7364      char *name;
7365 {
7366   if (*name == NULLCHAR) {
7367     return;
7368   } else if (strcmp(name, "$") == 0) {
7369     putc(BELLCHAR, stderr);
7370   } else {
7371     char buf[2048];
7372     snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
7373     system(buf);
7374   }
7375 }
7376
7377 void
7378 RingBell()
7379 {
7380   PlaySound(appData.soundMove);
7381 }
7382
7383 void
7384 PlayIcsWinSound()
7385 {
7386   PlaySound(appData.soundIcsWin);
7387 }
7388
7389 void
7390 PlayIcsLossSound()
7391 {
7392   PlaySound(appData.soundIcsLoss);
7393 }
7394
7395 void
7396 PlayIcsDrawSound()
7397 {
7398   PlaySound(appData.soundIcsDraw);
7399 }
7400
7401 void
7402 PlayIcsUnfinishedSound()
7403 {
7404   PlaySound(appData.soundIcsUnfinished);
7405 }
7406
7407 void
7408 PlayAlarmSound()
7409 {
7410   PlaySound(appData.soundIcsAlarm);
7411 }
7412
7413 void
7414 EchoOn()
7415 {
7416     system("stty echo");
7417 }
7418
7419 void
7420 EchoOff()
7421 {
7422     system("stty -echo");
7423 }
7424
7425 void
7426 Colorize(cc, continuation)
7427      ColorClass cc;
7428      int continuation;
7429 {
7430     char buf[MSG_SIZ];
7431     int count, outCount, error;
7432
7433     if (textColors[(int)cc].bg > 0) {
7434         if (textColors[(int)cc].fg > 0) {
7435           snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7436                    textColors[(int)cc].fg, textColors[(int)cc].bg);
7437         } else {
7438           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7439                    textColors[(int)cc].bg);
7440         }
7441     } else {
7442         if (textColors[(int)cc].fg > 0) {
7443           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7444                     textColors[(int)cc].fg);
7445         } else {
7446           snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7447         }
7448     }
7449     count = strlen(buf);
7450     outCount = OutputToProcess(NoProc, buf, count, &error);
7451     if (outCount < count) {
7452         DisplayFatalError(_("Error writing to display"), error, 1);
7453     }
7454
7455     if (continuation) return;
7456     switch (cc) {
7457     case ColorShout:
7458       PlaySound(appData.soundShout);
7459       break;
7460     case ColorSShout:
7461       PlaySound(appData.soundSShout);
7462       break;
7463     case ColorChannel1:
7464       PlaySound(appData.soundChannel1);
7465       break;
7466     case ColorChannel:
7467       PlaySound(appData.soundChannel);
7468       break;
7469     case ColorKibitz:
7470       PlaySound(appData.soundKibitz);
7471       break;
7472     case ColorTell:
7473       PlaySound(appData.soundTell);
7474       break;
7475     case ColorChallenge:
7476       PlaySound(appData.soundChallenge);
7477       break;
7478     case ColorRequest:
7479       PlaySound(appData.soundRequest);
7480       break;
7481     case ColorSeek:
7482       PlaySound(appData.soundSeek);
7483       break;
7484     case ColorNormal:
7485     case ColorNone:
7486     default:
7487       break;
7488     }
7489 }
7490
7491 char *UserName()
7492 {
7493     return getpwuid(getuid())->pw_name;
7494 }
7495
7496 static char *
7497 ExpandPathName(path)
7498      char *path;
7499 {
7500     static char static_buf[4*MSG_SIZ];
7501     char *d, *s, buf[4*MSG_SIZ];
7502     struct passwd *pwd;
7503
7504     s = path;
7505     d = static_buf;
7506
7507     while (*s && isspace(*s))
7508       ++s;
7509
7510     if (!*s) {
7511         *d = 0;
7512         return static_buf;
7513     }
7514
7515     if (*s == '~') {
7516         if (*(s+1) == '/') {
7517           safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7518           strcat(d, s+1);
7519         }
7520         else {
7521           safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7522           *strchr(buf, '/') = 0;
7523           pwd = getpwnam(buf);
7524           if (!pwd)
7525             {
7526               fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7527                       buf, path);
7528               return NULL;
7529             }
7530           safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7531           strcat(d, strchr(s+1, '/'));
7532         }
7533     }
7534     else
7535       safeStrCpy(d, s, 4*MSG_SIZ );
7536
7537     return static_buf;
7538 }
7539
7540 char *HostName()
7541 {
7542     static char host_name[MSG_SIZ];
7543
7544 #if HAVE_GETHOSTNAME
7545     gethostname(host_name, MSG_SIZ);
7546     return host_name;
7547 #else  /* not HAVE_GETHOSTNAME */
7548 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7549     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7550     return host_name;
7551 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7552     return "localhost";
7553 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7554 #endif /* not HAVE_GETHOSTNAME */
7555 }
7556
7557 XtIntervalId delayedEventTimerXID = 0;
7558 DelayedEventCallback delayedEventCallback = 0;
7559
7560 void
7561 FireDelayedEvent()
7562 {
7563     delayedEventTimerXID = 0;
7564     delayedEventCallback();
7565 }
7566
7567 void
7568 ScheduleDelayedEvent(cb, millisec)
7569      DelayedEventCallback cb; long millisec;
7570 {
7571     if(delayedEventTimerXID && delayedEventCallback == cb)
7572         // [HGM] alive: replace, rather than add or flush identical event
7573         XtRemoveTimeOut(delayedEventTimerXID);
7574     delayedEventCallback = cb;
7575     delayedEventTimerXID =
7576       XtAppAddTimeOut(appContext, millisec,
7577                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7578 }
7579
7580 DelayedEventCallback
7581 GetDelayedEvent()
7582 {
7583   if (delayedEventTimerXID) {
7584     return delayedEventCallback;
7585   } else {
7586     return NULL;
7587   }
7588 }
7589
7590 void
7591 CancelDelayedEvent()
7592 {
7593   if (delayedEventTimerXID) {
7594     XtRemoveTimeOut(delayedEventTimerXID);
7595     delayedEventTimerXID = 0;
7596   }
7597 }
7598
7599 XtIntervalId loadGameTimerXID = 0;
7600
7601 int LoadGameTimerRunning()
7602 {
7603     return loadGameTimerXID != 0;
7604 }
7605
7606 int StopLoadGameTimer()
7607 {
7608     if (loadGameTimerXID != 0) {
7609         XtRemoveTimeOut(loadGameTimerXID);
7610         loadGameTimerXID = 0;
7611         return TRUE;
7612     } else {
7613         return FALSE;
7614     }
7615 }
7616
7617 void
7618 LoadGameTimerCallback(arg, id)
7619      XtPointer arg;
7620      XtIntervalId *id;
7621 {
7622     loadGameTimerXID = 0;
7623     AutoPlayGameLoop();
7624 }
7625
7626 void
7627 StartLoadGameTimer(millisec)
7628      long millisec;
7629 {
7630     loadGameTimerXID =
7631       XtAppAddTimeOut(appContext, millisec,
7632                       (XtTimerCallbackProc) LoadGameTimerCallback,
7633                       (XtPointer) 0);
7634 }
7635
7636 XtIntervalId analysisClockXID = 0;
7637
7638 void
7639 AnalysisClockCallback(arg, id)
7640      XtPointer arg;
7641      XtIntervalId *id;
7642 {
7643     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7644          || appData.icsEngineAnalyze) { // [DM]
7645         AnalysisPeriodicEvent(0);
7646         StartAnalysisClock();
7647     }
7648 }
7649
7650 void
7651 StartAnalysisClock()
7652 {
7653     analysisClockXID =
7654       XtAppAddTimeOut(appContext, 2000,
7655                       (XtTimerCallbackProc) AnalysisClockCallback,
7656                       (XtPointer) 0);
7657 }
7658
7659 XtIntervalId clockTimerXID = 0;
7660
7661 int ClockTimerRunning()
7662 {
7663     return clockTimerXID != 0;
7664 }
7665
7666 int StopClockTimer()
7667 {
7668     if (clockTimerXID != 0) {
7669         XtRemoveTimeOut(clockTimerXID);
7670         clockTimerXID = 0;
7671         return TRUE;
7672     } else {
7673         return FALSE;
7674     }
7675 }
7676
7677 void
7678 ClockTimerCallback(arg, id)
7679      XtPointer arg;
7680      XtIntervalId *id;
7681 {
7682     clockTimerXID = 0;
7683     DecrementClocks();
7684 }
7685
7686 void
7687 StartClockTimer(millisec)
7688      long millisec;
7689 {
7690     clockTimerXID =
7691       XtAppAddTimeOut(appContext, millisec,
7692                       (XtTimerCallbackProc) ClockTimerCallback,
7693                       (XtPointer) 0);
7694 }
7695
7696 void
7697 DisplayTimerLabel(w, color, timer, highlight)
7698      Widget w;
7699      char *color;
7700      long timer;
7701      int highlight;
7702 {
7703     char buf[MSG_SIZ];
7704     Arg args[16];
7705
7706     /* check for low time warning */
7707     Pixel foregroundOrWarningColor = timerForegroundPixel;
7708
7709     if (timer > 0 &&
7710         appData.lowTimeWarning &&
7711         (timer / 1000) < appData.icsAlarmTime)
7712       foregroundOrWarningColor = lowTimeWarningColor;
7713
7714     if (appData.clockMode) {
7715       snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7716       XtSetArg(args[0], XtNlabel, buf);
7717     } else {
7718       snprintf(buf, MSG_SIZ, "%s  ", color);
7719       XtSetArg(args[0], XtNlabel, buf);
7720     }
7721
7722     if (highlight) {
7723
7724         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7725         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7726     } else {
7727         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7728         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7729     }
7730
7731     XtSetValues(w, args, 3);
7732 }
7733
7734 void
7735 DisplayWhiteClock(timeRemaining, highlight)
7736      long timeRemaining;
7737      int highlight;
7738 {
7739     Arg args[16];
7740
7741     if(appData.noGUI) return;
7742     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7743     if (highlight && iconPixmap == bIconPixmap) {
7744         iconPixmap = wIconPixmap;
7745         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7746         XtSetValues(shellWidget, args, 1);
7747     }
7748 }
7749
7750 void
7751 DisplayBlackClock(timeRemaining, highlight)
7752      long timeRemaining;
7753      int highlight;
7754 {
7755     Arg args[16];
7756
7757     if(appData.noGUI) return;
7758     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7759     if (highlight && iconPixmap == wIconPixmap) {
7760         iconPixmap = bIconPixmap;
7761         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7762         XtSetValues(shellWidget, args, 1);
7763     }
7764 }
7765
7766 #define CPNone 0
7767 #define CPReal 1
7768 #define CPComm 2
7769 #define CPSock 3
7770 #define CPLoop 4
7771 typedef int CPKind;
7772
7773 typedef struct {
7774     CPKind kind;
7775     int pid;
7776     int fdTo, fdFrom;
7777 } ChildProc;
7778
7779
7780 int StartChildProcess(cmdLine, dir, pr)
7781      char *cmdLine;
7782      char *dir;
7783      ProcRef *pr;
7784 {
7785     char *argv[64], *p;
7786     int i, pid;
7787     int to_prog[2], from_prog[2];
7788     ChildProc *cp;
7789     char buf[MSG_SIZ];
7790
7791     if (appData.debugMode) {
7792         fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7793     }
7794
7795     /* We do NOT feed the cmdLine to the shell; we just
7796        parse it into blank-separated arguments in the
7797        most simple-minded way possible.
7798        */
7799     i = 0;
7800     safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7801     p = buf;
7802     for (;;) {
7803         while(*p == ' ') p++;
7804         argv[i++] = p;
7805         if(*p == '"' || *p == '\'')
7806              p = strchr(++argv[i-1], *p);
7807         else p = strchr(p, ' ');
7808         if (p == NULL) break;
7809         *p++ = NULLCHAR;
7810     }
7811     argv[i] = NULL;
7812
7813     SetUpChildIO(to_prog, from_prog);
7814
7815     if ((pid = fork()) == 0) {
7816         /* Child process */
7817         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7818         close(to_prog[1]);     // first close the unused pipe ends
7819         close(from_prog[0]);
7820         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
7821         dup2(from_prog[1], 1);
7822         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7823         close(from_prog[1]);                   // and closing again loses one of the pipes!
7824         if(fileno(stderr) >= 2) // better safe than sorry...
7825                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7826
7827         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7828             perror(dir);
7829             exit(1);
7830         }
7831
7832         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7833
7834         execvp(argv[0], argv);
7835
7836         /* If we get here, exec failed */
7837         perror(argv[0]);
7838         exit(1);
7839     }
7840
7841     /* Parent process */
7842     close(to_prog[0]);
7843     close(from_prog[1]);
7844
7845     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7846     cp->kind = CPReal;
7847     cp->pid = pid;
7848     cp->fdFrom = from_prog[0];
7849     cp->fdTo = to_prog[1];
7850     *pr = (ProcRef) cp;
7851     return 0;
7852 }
7853
7854 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7855 static RETSIGTYPE AlarmCallBack(int n)
7856 {
7857     return;
7858 }
7859
7860 void
7861 DestroyChildProcess(pr, signalType)
7862      ProcRef pr;
7863      int signalType;
7864 {
7865     ChildProc *cp = (ChildProc *) pr;
7866
7867     if (cp->kind != CPReal) return;
7868     cp->kind = CPNone;
7869     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7870         signal(SIGALRM, AlarmCallBack);
7871         alarm(3);
7872         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7873             kill(cp->pid, SIGKILL); // kill it forcefully
7874             wait((int *) 0);        // and wait again
7875         }
7876     } else {
7877         if (signalType) {
7878             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7879         }
7880         /* Process is exiting either because of the kill or because of
7881            a quit command sent by the backend; either way, wait for it to die.
7882         */
7883         wait((int *) 0);
7884     }
7885     close(cp->fdFrom);
7886     close(cp->fdTo);
7887 }
7888
7889 void
7890 InterruptChildProcess(pr)
7891      ProcRef pr;
7892 {
7893     ChildProc *cp = (ChildProc *) pr;
7894
7895     if (cp->kind != CPReal) return;
7896     (void) kill(cp->pid, SIGINT); /* stop it thinking */
7897 }
7898
7899 int OpenTelnet(host, port, pr)
7900      char *host;
7901      char *port;
7902      ProcRef *pr;
7903 {
7904     char cmdLine[MSG_SIZ];
7905
7906     if (port[0] == NULLCHAR) {
7907       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7908     } else {
7909       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7910     }
7911     return StartChildProcess(cmdLine, "", pr);
7912 }
7913
7914 int OpenTCP(host, port, pr)
7915      char *host;
7916      char *port;
7917      ProcRef *pr;
7918 {
7919 #if OMIT_SOCKETS
7920     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7921 #else  /* !OMIT_SOCKETS */
7922     int s;
7923     struct sockaddr_in sa;
7924     struct hostent     *hp;
7925     unsigned short uport;
7926     ChildProc *cp;
7927
7928     if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
7929         return errno;
7930     }
7931
7932     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7933     sa.sin_family = AF_INET;
7934     sa.sin_addr.s_addr = INADDR_ANY;
7935     uport = (unsigned short) 0;
7936     sa.sin_port = htons(uport);
7937     if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
7938         return errno;
7939     }
7940
7941     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7942     if (!(hp = gethostbyname(host))) {
7943         int b0, b1, b2, b3;
7944         if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
7945             hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7946             hp->h_addrtype = AF_INET;
7947             hp->h_length = 4;
7948             hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7949             hp->h_addr_list[0] = (char *) malloc(4);
7950             hp->h_addr_list[0][0] = b0;
7951             hp->h_addr_list[0][1] = b1;
7952             hp->h_addr_list[0][2] = b2;
7953             hp->h_addr_list[0][3] = b3;
7954         } else {
7955             return ENOENT;
7956         }
7957     }
7958     sa.sin_family = hp->h_addrtype;
7959     uport = (unsigned short) atoi(port);
7960     sa.sin_port = htons(uport);
7961     memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7962
7963     if (connect(s, (struct sockaddr *) &sa,
7964                 sizeof(struct sockaddr_in)) < 0) {
7965         return errno;
7966     }
7967
7968     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7969     cp->kind = CPSock;
7970     cp->pid = 0;
7971     cp->fdFrom = s;
7972     cp->fdTo = s;
7973     *pr = (ProcRef) cp;
7974
7975 #endif /* !OMIT_SOCKETS */
7976
7977     return 0;
7978 }
7979
7980 int OpenCommPort(name, pr)
7981      char *name;
7982      ProcRef *pr;
7983 {
7984     int fd;
7985     ChildProc *cp;
7986
7987     fd = open(name, 2, 0);
7988     if (fd < 0) return errno;
7989
7990     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7991     cp->kind = CPComm;
7992     cp->pid = 0;
7993     cp->fdFrom = fd;
7994     cp->fdTo = fd;
7995     *pr = (ProcRef) cp;
7996
7997     return 0;
7998 }
7999
8000 int OpenLoopback(pr)
8001      ProcRef *pr;
8002 {
8003     ChildProc *cp;
8004     int to[2], from[2];
8005
8006     SetUpChildIO(to, from);
8007
8008     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8009     cp->kind = CPLoop;
8010     cp->pid = 0;
8011     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
8012     cp->fdTo = to[1];
8013     *pr = (ProcRef) cp;
8014
8015     return 0;
8016 }
8017
8018 int OpenRcmd(host, user, cmd, pr)
8019      char *host, *user, *cmd;
8020      ProcRef *pr;
8021 {
8022     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
8023     return -1;
8024 }
8025
8026 #define INPUT_SOURCE_BUF_SIZE 8192
8027
8028 typedef struct {
8029     CPKind kind;
8030     int fd;
8031     int lineByLine;
8032     char *unused;
8033     InputCallback func;
8034     XtInputId xid;
8035     char buf[INPUT_SOURCE_BUF_SIZE];
8036     VOIDSTAR closure;
8037 } InputSource;
8038
8039 void
8040 DoInputCallback(closure, source, xid)
8041      caddr_t closure;
8042      int *source;
8043      XtInputId *xid;
8044 {
8045     InputSource *is = (InputSource *) closure;
8046     int count;
8047     int error;
8048     char *p, *q;
8049
8050     if (is->lineByLine) {
8051         count = read(is->fd, is->unused,
8052                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
8053         if (count <= 0) {
8054             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
8055             return;
8056         }
8057         is->unused += count;
8058         p = is->buf;
8059         while (p < is->unused) {
8060             q = memchr(p, '\n', is->unused - p);
8061             if (q == NULL) break;
8062             q++;
8063             (is->func)(is, is->closure, p, q - p, 0);
8064             p = q;
8065         }
8066         q = is->buf;
8067         while (p < is->unused) {
8068             *q++ = *p++;
8069         }
8070         is->unused = q;
8071     } else {
8072         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
8073         if (count == -1)
8074           error = errno;
8075         else
8076           error = 0;
8077         (is->func)(is, is->closure, is->buf, count, error);
8078     }
8079 }
8080
8081 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
8082      ProcRef pr;
8083      int lineByLine;
8084      InputCallback func;
8085      VOIDSTAR closure;
8086 {
8087     InputSource *is;
8088     ChildProc *cp = (ChildProc *) pr;
8089
8090     is = (InputSource *) calloc(1, sizeof(InputSource));
8091     is->lineByLine = lineByLine;
8092     is->func = func;
8093     if (pr == NoProc) {
8094         is->kind = CPReal;
8095         is->fd = fileno(stdin);
8096     } else {
8097         is->kind = cp->kind;
8098         is->fd = cp->fdFrom;
8099     }
8100     if (lineByLine) {
8101         is->unused = is->buf;
8102     }
8103
8104     is->xid = XtAppAddInput(appContext, is->fd,
8105                             (XtPointer) (XtInputReadMask),
8106                             (XtInputCallbackProc) DoInputCallback,
8107                             (XtPointer) is);
8108     is->closure = closure;
8109     return (InputSourceRef) is;
8110 }
8111
8112 void
8113 RemoveInputSource(isr)
8114      InputSourceRef isr;
8115 {
8116     InputSource *is = (InputSource *) isr;
8117
8118     if (is->xid == 0) return;
8119     XtRemoveInput(is->xid);
8120     is->xid = 0;
8121 }
8122
8123 int OutputToProcess(pr, message, count, outError)
8124      ProcRef pr;
8125      char *message;
8126      int count;
8127      int *outError;
8128 {
8129     static int line = 0;
8130     ChildProc *cp = (ChildProc *) pr;
8131     int outCount;
8132
8133     if (pr == NoProc)
8134     {
8135         if (appData.noJoin || !appData.useInternalWrap)
8136             outCount = fwrite(message, 1, count, stdout);
8137         else
8138         {
8139             int width = get_term_width();
8140             int len = wrap(NULL, message, count, width, &line);
8141             char *msg = malloc(len);
8142             int dbgchk;
8143
8144             if (!msg)
8145                 outCount = fwrite(message, 1, count, stdout);
8146             else
8147             {
8148                 dbgchk = wrap(msg, message, count, width, &line);
8149                 if (dbgchk != len && appData.debugMode)
8150                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
8151                 outCount = fwrite(msg, 1, dbgchk, stdout);
8152                 free(msg);
8153             }
8154         }
8155     }
8156     else
8157       outCount = write(cp->fdTo, message, count);
8158
8159     if (outCount == -1)
8160       *outError = errno;
8161     else
8162       *outError = 0;
8163
8164     return outCount;
8165 }
8166
8167 /* Output message to process, with "ms" milliseconds of delay
8168    between each character. This is needed when sending the logon
8169    script to ICC, which for some reason doesn't like the
8170    instantaneous send. */
8171 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8172      ProcRef pr;
8173      char *message;
8174      int count;
8175      int *outError;
8176      long msdelay;
8177 {
8178     ChildProc *cp = (ChildProc *) pr;
8179     int outCount = 0;
8180     int r;
8181
8182     while (count--) {
8183         r = write(cp->fdTo, message++, 1);
8184         if (r == -1) {
8185             *outError = errno;
8186             return outCount;
8187         }
8188         ++outCount;
8189         if (msdelay >= 0)
8190           TimeDelay(msdelay);
8191     }
8192
8193     return outCount;
8194 }
8195
8196 /****   Animation code by Hugh Fisher, DCS, ANU.
8197
8198         Known problem: if a window overlapping the board is
8199         moved away while a piece is being animated underneath,
8200         the newly exposed area won't be updated properly.
8201         I can live with this.
8202
8203         Known problem: if you look carefully at the animation
8204         of pieces in mono mode, they are being drawn as solid
8205         shapes without interior detail while moving. Fixing
8206         this would be a major complication for minimal return.
8207 ****/
8208
8209 /*      Masks for XPM pieces. Black and white pieces can have
8210         different shapes, but in the interest of retaining my
8211         sanity pieces must have the same outline on both light
8212         and dark squares, and all pieces must use the same
8213         background square colors/images.                */
8214
8215 static int xpmDone = 0;
8216
8217 static void
8218 CreateAnimMasks (pieceDepth)
8219      int pieceDepth;
8220 {
8221   ChessSquare   piece;
8222   Pixmap        buf;
8223   GC            bufGC, maskGC;
8224   int           kind, n;
8225   unsigned long plane;
8226   XGCValues     values;
8227
8228   /* Need a bitmap just to get a GC with right depth */
8229   buf = XCreatePixmap(xDisplay, xBoardWindow,
8230                         8, 8, 1);
8231   values.foreground = 1;
8232   values.background = 0;
8233   /* Don't use XtGetGC, not read only */
8234   maskGC = XCreateGC(xDisplay, buf,
8235                     GCForeground | GCBackground, &values);
8236   XFreePixmap(xDisplay, buf);
8237
8238   buf = XCreatePixmap(xDisplay, xBoardWindow,
8239                       squareSize, squareSize, pieceDepth);
8240   values.foreground = XBlackPixel(xDisplay, xScreen);
8241   values.background = XWhitePixel(xDisplay, xScreen);
8242   bufGC = XCreateGC(xDisplay, buf,
8243                     GCForeground | GCBackground, &values);
8244
8245   for (piece = WhitePawn; piece <= BlackKing; piece++) {
8246     /* Begin with empty mask */
8247     if(!xpmDone) // [HGM] pieces: keep using existing
8248     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8249                                  squareSize, squareSize, 1);
8250     XSetFunction(xDisplay, maskGC, GXclear);
8251     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8252                    0, 0, squareSize, squareSize);
8253
8254     /* Take a copy of the piece */
8255     if (White(piece))
8256       kind = 0;
8257     else
8258       kind = 2;
8259     XSetFunction(xDisplay, bufGC, GXcopy);
8260     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8261               buf, bufGC,
8262               0, 0, squareSize, squareSize, 0, 0);
8263
8264     /* XOR the background (light) over the piece */
8265     XSetFunction(xDisplay, bufGC, GXxor);
8266     if (useImageSqs)
8267       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8268                 0, 0, squareSize, squareSize, 0, 0);
8269     else {
8270       XSetForeground(xDisplay, bufGC, lightSquareColor);
8271       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8272     }
8273
8274     /* We now have an inverted piece image with the background
8275        erased. Construct mask by just selecting all the non-zero
8276        pixels - no need to reconstruct the original image.      */
8277     XSetFunction(xDisplay, maskGC, GXor);
8278     plane = 1;
8279     /* Might be quicker to download an XImage and create bitmap
8280        data from it rather than this N copies per piece, but it
8281        only takes a fraction of a second and there is a much
8282        longer delay for loading the pieces.             */
8283     for (n = 0; n < pieceDepth; n ++) {
8284       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8285                  0, 0, squareSize, squareSize,
8286                  0, 0, plane);
8287       plane = plane << 1;
8288     }
8289   }
8290   /* Clean up */
8291   XFreePixmap(xDisplay, buf);
8292   XFreeGC(xDisplay, bufGC);
8293   XFreeGC(xDisplay, maskGC);
8294 }
8295
8296 static void
8297 InitAnimState (anim, info)
8298   AnimState * anim;
8299   XWindowAttributes * info;
8300 {
8301   XtGCMask  mask;
8302   XGCValues values;
8303
8304   /* Each buffer is square size, same depth as window */
8305   anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8306                         squareSize, squareSize, info->depth);
8307   anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8308                         squareSize, squareSize, info->depth);
8309
8310   /* Create a plain GC for blitting */
8311   mask = GCForeground | GCBackground | GCFunction |
8312          GCPlaneMask | GCGraphicsExposures;
8313   values.foreground = XBlackPixel(xDisplay, xScreen);
8314   values.background = XWhitePixel(xDisplay, xScreen);
8315   values.function   = GXcopy;
8316   values.plane_mask = AllPlanes;
8317   values.graphics_exposures = False;
8318   anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8319
8320   /* Piece will be copied from an existing context at
8321      the start of each new animation/drag. */
8322   anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8323
8324   /* Outline will be a read-only copy of an existing */
8325   anim->outlineGC = None;
8326 }
8327
8328 static void
8329 CreateAnimVars ()
8330 {
8331   static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
8332   XWindowAttributes info;
8333
8334   if (xpmDone && gameInfo.variant == old) return;
8335   if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
8336   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8337
8338   InitAnimState(&game, &info);
8339   InitAnimState(&player, &info);
8340
8341   /* For XPM pieces, we need bitmaps to use as masks. */
8342   if (useImages)
8343     CreateAnimMasks(info.depth);
8344    xpmDone = 1;
8345 }
8346
8347 #ifndef HAVE_USLEEP
8348
8349 static Boolean frameWaiting;
8350
8351 static RETSIGTYPE FrameAlarm (sig)
8352      int sig;
8353 {
8354   frameWaiting = False;
8355   /* In case System-V style signals.  Needed?? */
8356   signal(SIGALRM, FrameAlarm);
8357 }
8358
8359 static void
8360 FrameDelay (time)
8361      int time;
8362 {
8363   struct itimerval delay;
8364
8365   XSync(xDisplay, False);
8366
8367   if (time > 0) {
8368     frameWaiting = True;
8369     signal(SIGALRM, FrameAlarm);
8370     delay.it_interval.tv_sec =
8371       delay.it_value.tv_sec = time / 1000;
8372     delay.it_interval.tv_usec =
8373       delay.it_value.tv_usec = (time % 1000) * 1000;
8374     setitimer(ITIMER_REAL, &delay, NULL);
8375     while (frameWaiting) pause();
8376     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8377     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8378     setitimer(ITIMER_REAL, &delay, NULL);
8379   }
8380 }
8381
8382 #else
8383
8384 static void
8385 FrameDelay (time)
8386      int time;
8387 {
8388   XSync(xDisplay, False);
8389   if (time > 0)
8390     usleep(time * 1000);
8391 }
8392
8393 #endif
8394
8395 /*      Convert board position to corner of screen rect and color       */
8396
8397 static void
8398 ScreenSquare(column, row, pt, color)
8399      int column; int row; XPoint * pt; int * color;
8400 {
8401   if (flipView) {
8402     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8403     pt->y = lineGap + row * (squareSize + lineGap);
8404   } else {
8405     pt->x = lineGap + column * (squareSize + lineGap);
8406     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8407   }
8408   *color = SquareColor(row, column);
8409 }
8410
8411 /*      Convert window coords to square                 */
8412
8413 static void
8414 BoardSquare(x, y, column, row)
8415      int x; int y; int * column; int * row;
8416 {
8417   *column = EventToSquare(x, BOARD_WIDTH);
8418   if (flipView && *column >= 0)
8419     *column = BOARD_WIDTH - 1 - *column;
8420   *row = EventToSquare(y, BOARD_HEIGHT);
8421   if (!flipView && *row >= 0)
8422     *row = BOARD_HEIGHT - 1 - *row;
8423 }
8424
8425 /*   Utilities  */
8426
8427 #undef Max  /* just in case */
8428 #undef Min
8429 #define Max(a, b) ((a) > (b) ? (a) : (b))
8430 #define Min(a, b) ((a) < (b) ? (a) : (b))
8431
8432 static void
8433 SetRect(rect, x, y, width, height)
8434      XRectangle * rect; int x; int y; int width; int height;
8435 {
8436   rect->x = x;
8437   rect->y = y;
8438   rect->width  = width;
8439   rect->height = height;
8440 }
8441
8442 /*      Test if two frames overlap. If they do, return
8443         intersection rect within old and location of
8444         that rect within new. */
8445
8446 static Boolean
8447 Intersect(old, new, size, area, pt)
8448      XPoint * old; XPoint * new;
8449      int size; XRectangle * area; XPoint * pt;
8450 {
8451   if (old->x > new->x + size || new->x > old->x + size ||
8452       old->y > new->y + size || new->y > old->y + size) {
8453     return False;
8454   } else {
8455     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8456             size - abs(old->x - new->x), size - abs(old->y - new->y));
8457     pt->x = Max(old->x - new->x, 0);
8458     pt->y = Max(old->y - new->y, 0);
8459     return True;
8460   }
8461 }
8462
8463 /*      For two overlapping frames, return the rect(s)
8464         in the old that do not intersect with the new.   */
8465
8466 static void
8467 CalcUpdateRects(old, new, size, update, nUpdates)
8468      XPoint * old; XPoint * new; int size;
8469      XRectangle update[]; int * nUpdates;
8470 {
8471   int        count;
8472
8473   /* If old = new (shouldn't happen) then nothing to draw */
8474   if (old->x == new->x && old->y == new->y) {
8475     *nUpdates = 0;
8476     return;
8477   }
8478   /* Work out what bits overlap. Since we know the rects
8479      are the same size we don't need a full intersect calc. */
8480   count = 0;
8481   /* Top or bottom edge? */
8482   if (new->y > old->y) {
8483     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8484     count ++;
8485   } else if (old->y > new->y) {
8486     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8487                               size, old->y - new->y);
8488     count ++;
8489   }
8490   /* Left or right edge - don't overlap any update calculated above. */
8491   if (new->x > old->x) {
8492     SetRect(&(update[count]), old->x, Max(new->y, old->y),
8493                               new->x - old->x, size - abs(new->y - old->y));
8494     count ++;
8495   } else if (old->x > new->x) {
8496     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8497                               old->x - new->x, size - abs(new->y - old->y));
8498     count ++;
8499   }
8500   /* Done */
8501   *nUpdates = count;
8502 }
8503
8504 /*      Generate a series of frame coords from start->mid->finish.
8505         The movement rate doubles until the half way point is
8506         reached, then halves back down to the final destination,
8507         which gives a nice slow in/out effect. The algorithmn
8508         may seem to generate too many intermediates for short
8509         moves, but remember that the purpose is to attract the
8510         viewers attention to the piece about to be moved and
8511         then to where it ends up. Too few frames would be less
8512         noticeable.                                             */
8513
8514 static void
8515 Tween(start, mid, finish, factor, frames, nFrames)
8516      XPoint * start; XPoint * mid;
8517      XPoint * finish; int factor;
8518      XPoint frames[]; int * nFrames;
8519 {
8520   int fraction, n, count;
8521
8522   count = 0;
8523
8524   /* Slow in, stepping 1/16th, then 1/8th, ... */
8525   fraction = 1;
8526   for (n = 0; n < factor; n++)
8527     fraction *= 2;
8528   for (n = 0; n < factor; n++) {
8529     frames[count].x = start->x + (mid->x - start->x) / fraction;
8530     frames[count].y = start->y + (mid->y - start->y) / fraction;
8531     count ++;
8532     fraction = fraction / 2;
8533   }
8534
8535   /* Midpoint */
8536   frames[count] = *mid;
8537   count ++;
8538
8539   /* Slow out, stepping 1/2, then 1/4, ... */
8540   fraction = 2;
8541   for (n = 0; n < factor; n++) {
8542     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8543     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8544     count ++;
8545     fraction = fraction * 2;
8546   }
8547   *nFrames = count;
8548 }
8549
8550 /*      Draw a piece on the screen without disturbing what's there      */
8551
8552 static void
8553 SelectGCMask(piece, clip, outline, mask)
8554      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8555 {
8556   GC source;
8557
8558   /* Bitmap for piece being moved. */
8559   if (appData.monoMode) {
8560       *mask = *pieceToSolid(piece);
8561   } else if (useImages) {
8562 #if HAVE_LIBXPM
8563       *mask = xpmMask[piece];
8564 #else
8565       *mask = ximMaskPm[piece];
8566 #endif
8567   } else {
8568       *mask = *pieceToSolid(piece);
8569   }
8570
8571   /* GC for piece being moved. Square color doesn't matter, but
8572      since it gets modified we make a copy of the original. */
8573   if (White(piece)) {
8574     if (appData.monoMode)
8575       source = bwPieceGC;
8576     else
8577       source = wlPieceGC;
8578   } else {
8579     if (appData.monoMode)
8580       source = wbPieceGC;
8581     else
8582       source = blPieceGC;
8583   }
8584   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8585
8586   /* Outline only used in mono mode and is not modified */
8587   if (White(piece))
8588     *outline = bwPieceGC;
8589   else
8590     *outline = wbPieceGC;
8591 }
8592
8593 static void
8594 OverlayPiece(piece, clip, outline,  dest)
8595      ChessSquare piece; GC clip; GC outline; Drawable dest;
8596 {
8597   int   kind;
8598
8599   if (!useImages) {
8600     /* Draw solid rectangle which will be clipped to shape of piece */
8601     XFillRectangle(xDisplay, dest, clip,
8602                    0, 0, squareSize, squareSize);
8603     if (appData.monoMode)
8604       /* Also draw outline in contrasting color for black
8605          on black / white on white cases                */
8606       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8607                  0, 0, squareSize, squareSize, 0, 0, 1);
8608   } else {
8609     /* Copy the piece */
8610     if (White(piece))
8611       kind = 0;
8612     else
8613       kind = 2;
8614     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8615               dest, clip,
8616               0, 0, squareSize, squareSize,
8617               0, 0);
8618   }
8619 }
8620
8621 /* Animate the movement of a single piece */
8622
8623 static void
8624 BeginAnimation(anim, piece, startColor, start)
8625      AnimState *anim;
8626      ChessSquare piece;
8627      int startColor;
8628      XPoint * start;
8629 {
8630   Pixmap mask;
8631
8632   /* The old buffer is initialised with the start square (empty) */
8633   BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
8634   anim->prevFrame = *start;
8635
8636   /* The piece will be drawn using its own bitmap as a matte    */
8637   SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8638   XSetClipMask(xDisplay, anim->pieceGC, mask);
8639 }
8640
8641 static void
8642 AnimationFrame(anim, frame, piece)
8643      AnimState *anim;
8644      XPoint *frame;
8645      ChessSquare piece;
8646 {
8647   XRectangle updates[4];
8648   XRectangle overlap;
8649   XPoint     pt;
8650   int        count, i;
8651
8652   /* Save what we are about to draw into the new buffer */
8653   XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8654             frame->x, frame->y, squareSize, squareSize,
8655             0, 0);
8656
8657   /* Erase bits of the previous frame */
8658   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8659     /* Where the new frame overlapped the previous,
8660        the contents in newBuf are wrong. */
8661     XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8662               overlap.x, overlap.y,
8663               overlap.width, overlap.height,
8664               pt.x, pt.y);
8665     /* Repaint the areas in the old that don't overlap new */
8666     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8667     for (i = 0; i < count; i++)
8668       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8669                 updates[i].x - anim->prevFrame.x,
8670                 updates[i].y - anim->prevFrame.y,
8671                 updates[i].width, updates[i].height,
8672                 updates[i].x, updates[i].y);
8673   } else {
8674     /* Easy when no overlap */
8675     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8676                   0, 0, squareSize, squareSize,
8677                   anim->prevFrame.x, anim->prevFrame.y);
8678   }
8679
8680   /* Save this frame for next time round */
8681   XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8682                 0, 0, squareSize, squareSize,
8683                 0, 0);
8684   anim->prevFrame = *frame;
8685
8686   /* Draw piece over original screen contents, not current,
8687      and copy entire rect. Wipes out overlapping piece images. */
8688   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8689   XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8690                 0, 0, squareSize, squareSize,
8691                 frame->x, frame->y);
8692 }
8693
8694 static void
8695 EndAnimation (anim, finish)
8696      AnimState *anim;
8697      XPoint *finish;
8698 {
8699   XRectangle updates[4];
8700   XRectangle overlap;
8701   XPoint     pt;
8702   int        count, i;
8703
8704   /* The main code will redraw the final square, so we
8705      only need to erase the bits that don't overlap.    */
8706   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8707     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8708     for (i = 0; i < count; i++)
8709       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8710                 updates[i].x - anim->prevFrame.x,
8711                 updates[i].y - anim->prevFrame.y,
8712                 updates[i].width, updates[i].height,
8713                 updates[i].x, updates[i].y);
8714   } else {
8715     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8716                 0, 0, squareSize, squareSize,
8717                 anim->prevFrame.x, anim->prevFrame.y);
8718   }
8719 }
8720
8721 static void
8722 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8723      AnimState *anim;
8724      ChessSquare piece; int startColor;
8725      XPoint * start; XPoint * finish;
8726      XPoint frames[]; int nFrames;
8727 {
8728   int n;
8729
8730   BeginAnimation(anim, piece, startColor, start);
8731   for (n = 0; n < nFrames; n++) {
8732     AnimationFrame(anim, &(frames[n]), piece);
8733     FrameDelay(appData.animSpeed);
8734   }
8735   EndAnimation(anim, finish);
8736 }
8737
8738 /* Main control logic for deciding what to animate and how */
8739
8740 void
8741 AnimateMove(board, fromX, fromY, toX, toY)
8742      Board board;
8743      int fromX;
8744      int fromY;
8745      int toX;
8746      int toY;
8747 {
8748   ChessSquare piece;
8749   int hop;
8750   XPoint      start, finish, mid;
8751   XPoint      frames[kFactor * 2 + 1];
8752   int         nFrames, startColor, endColor;
8753
8754   /* Are we animating? */
8755   if (!appData.animate || appData.blindfold)
8756     return;
8757
8758   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8759      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8760         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8761
8762   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8763   piece = board[fromY][fromX];
8764   if (piece >= EmptySquare) return;
8765
8766 #if DONT_HOP
8767   hop = FALSE;
8768 #else
8769   hop = (piece == WhiteKnight || piece == BlackKnight);
8770 #endif
8771
8772   if (appData.debugMode) {
8773       fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8774                              _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8775              piece, fromX, fromY, toX, toY);  }
8776
8777   ScreenSquare(fromX, fromY, &start, &startColor);
8778   ScreenSquare(toX, toY, &finish, &endColor);
8779
8780   if (hop) {
8781     /* Knight: make diagonal movement then straight */
8782     if (abs(toY - fromY) < abs(toX - fromX)) {
8783        mid.x = start.x + (finish.x - start.x) / 2;
8784        mid.y = finish.y;
8785      } else {
8786        mid.x = finish.x;
8787        mid.y = start.y + (finish.y - start.y) / 2;
8788      }
8789   } else {
8790     mid.x = start.x + (finish.x - start.x) / 2;
8791     mid.y = start.y + (finish.y - start.y) / 2;
8792   }
8793
8794   /* Don't use as many frames for very short moves */
8795   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8796     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8797   else
8798     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8799   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8800
8801   /* Be sure end square is redrawn */
8802   damage[0][toY][toX] = True;
8803 }
8804
8805 void
8806 DragPieceBegin(x, y)
8807      int x; int y;
8808 {
8809     int  boardX, boardY, color;
8810     XPoint corner;
8811
8812     /* Are we animating? */
8813     if (!appData.animateDragging || appData.blindfold)
8814       return;
8815
8816     /* Figure out which square we start in and the
8817        mouse position relative to top left corner. */
8818     BoardSquare(x, y, &boardX, &boardY);
8819     player.startBoardX = boardX;
8820     player.startBoardY = boardY;
8821     ScreenSquare(boardX, boardY, &corner, &color);
8822     player.startSquare  = corner;
8823     player.startColor   = color;
8824     /* As soon as we start dragging, the piece will jump slightly to
8825        be centered over the mouse pointer. */
8826     player.mouseDelta.x = squareSize/2;
8827     player.mouseDelta.y = squareSize/2;
8828     /* Initialise animation */
8829     player.dragPiece = PieceForSquare(boardX, boardY);
8830     /* Sanity check */
8831     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8832         player.dragActive = True;
8833         BeginAnimation(&player, player.dragPiece, color, &corner);
8834         /* Mark this square as needing to be redrawn. Note that
8835            we don't remove the piece though, since logically (ie
8836            as seen by opponent) the move hasn't been made yet. */
8837            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8838               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8839            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8840                      corner.x, corner.y, squareSize, squareSize,
8841                      0, 0); // [HGM] zh: unstack in stead of grab
8842         damage[0][boardY][boardX] = True;
8843     } else {
8844         player.dragActive = False;
8845     }
8846 }
8847
8848 static void
8849 DragPieceMove(x, y)
8850      int x; int y;
8851 {
8852     XPoint corner;
8853
8854     /* Are we animating? */
8855     if (!appData.animateDragging || appData.blindfold)
8856       return;
8857
8858     /* Sanity check */
8859     if (! player.dragActive)
8860       return;
8861     /* Move piece, maintaining same relative position
8862        of mouse within square    */
8863     corner.x = x - player.mouseDelta.x;
8864     corner.y = y - player.mouseDelta.y;
8865     AnimationFrame(&player, &corner, player.dragPiece);
8866 #if HIGHDRAG*0
8867     if (appData.highlightDragging) {
8868         int boardX, boardY;
8869         BoardSquare(x, y, &boardX, &boardY);
8870         SetHighlights(fromX, fromY, boardX, boardY);
8871     }
8872 #endif
8873 }
8874
8875 void
8876 DragPieceEnd(x, y)
8877      int x; int y;
8878 {
8879     int boardX, boardY, color;
8880     XPoint corner;
8881
8882     /* Are we animating? */
8883     if (!appData.animateDragging || appData.blindfold)
8884       return;
8885
8886     /* Sanity check */
8887     if (! player.dragActive)
8888       return;
8889     /* Last frame in sequence is square piece is
8890        placed on, which may not match mouse exactly. */
8891     BoardSquare(x, y, &boardX, &boardY);
8892     ScreenSquare(boardX, boardY, &corner, &color);
8893     EndAnimation(&player, &corner);
8894
8895     /* Be sure end square is redrawn */
8896     damage[0][boardY][boardX] = True;
8897
8898     /* This prevents weird things happening with fast successive
8899        clicks which on my Sun at least can cause motion events
8900        without corresponding press/release. */
8901     player.dragActive = False;
8902 }
8903
8904 /* Handle expose event while piece being dragged */
8905
8906 static void
8907 DrawDragPiece ()
8908 {
8909   if (!player.dragActive || appData.blindfold)
8910     return;
8911
8912   /* What we're doing: logically, the move hasn't been made yet,
8913      so the piece is still in it's original square. But visually
8914      it's being dragged around the board. So we erase the square
8915      that the piece is on and draw it at the last known drag point. */
8916   BlankSquare(player.startSquare.x, player.startSquare.y,
8917                 player.startColor, EmptySquare, xBoardWindow);
8918   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8919   damage[0][player.startBoardY][player.startBoardX] = TRUE;
8920 }
8921
8922 #include <sys/ioctl.h>
8923 int get_term_width()
8924 {
8925     int fd, default_width;
8926
8927     fd = STDIN_FILENO;
8928     default_width = 79; // this is FICS default anyway...
8929
8930 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8931     struct ttysize win;
8932     if (!ioctl(fd, TIOCGSIZE, &win))
8933         default_width = win.ts_cols;
8934 #elif defined(TIOCGWINSZ)
8935     struct winsize win;
8936     if (!ioctl(fd, TIOCGWINSZ, &win))
8937         default_width = win.ws_col;
8938 #endif
8939     return default_width;
8940 }
8941
8942 void
8943 update_ics_width()
8944 {
8945   static int old_width = 0;
8946   int new_width = get_term_width();
8947
8948   if (old_width != new_width)
8949     ics_printf("set width %d\n", new_width);
8950   old_width = new_width;
8951 }
8952
8953 void NotifyFrontendLogin()
8954 {
8955     update_ics_width();
8956 }