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