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