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