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