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