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