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