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