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