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