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