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