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