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