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