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