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