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