7893a4f3d4bba985fdc65b30b90125cc2e46e9cb
[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
2187     gtk_builder_connect_signals (builder, NULL);
2188
2189     // don't unref the builder, since we use it to get references to widgets
2190     //    g_object_unref (G_OBJECT (builder));
2191
2192     /* end parse glade file */
2193
2194     if (argc > 1)
2195       {
2196         fprintf(stderr, _("%s: unrecognized argument %s\n"),
2197                 programName, argv[1]);
2198
2199         fprintf(stderr, "Recognized options:\n");
2200         for(i = 0; i < XtNumber(shellOptions); i++) 
2201           {
2202             /* print first column */
2203             j = fprintf(stderr, "  %s%s", shellOptions[i].option,
2204                         (shellOptions[i].argKind == XrmoptionSepArg
2205                          ? " ARG" : ""));
2206             /* print second column and end line */
2207             if (++i < XtNumber(shellOptions)) 
2208               {         
2209                 fprintf(stderr, "%*c%s%s\n", 40 - j, ' ',
2210                         shellOptions[i].option,
2211                         (shellOptions[i].argKind == XrmoptionSepArg
2212                          ? " ARG" : ""));
2213               } 
2214             else 
2215               {
2216                 fprintf(stderr, "\n");
2217               };
2218           };
2219         exit(2);
2220       };
2221
2222     p = getenv("HOME");
2223     if (p == NULL) p = "/tmp";
2224     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
2225     gameCopyFilename = (char*) malloc(i);
2226     gamePasteFilename = (char*) malloc(i);
2227     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
2228     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2229
2230     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2231                               clientResources, XtNumber(clientResources),
2232                               NULL, 0);
2233
2234     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2235         static char buf[MSG_SIZ];
2236         EscapeExpand(buf, appData.initString);
2237         appData.initString = strdup(buf);
2238         EscapeExpand(buf, appData.secondInitString);
2239         appData.secondInitString = strdup(buf);
2240         EscapeExpand(buf, appData.firstComputerString);
2241         appData.firstComputerString = strdup(buf);
2242         EscapeExpand(buf, appData.secondComputerString);
2243         appData.secondComputerString = strdup(buf);
2244     }
2245
2246     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2247         chessDir = ".";
2248     } else {
2249         if (chdir(chessDir) != 0) {
2250             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2251             perror(chessDir);
2252             exit(1);
2253         }
2254     }
2255
2256     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2257         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2258         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
2259            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2260            exit(errno);
2261         }
2262         setbuf(debugFP, NULL);
2263     }
2264
2265     /* [HGM,HR] make sure board size is acceptable */
2266     if(appData.NrFiles > BOARD_SIZE ||
2267        appData.NrRanks > BOARD_SIZE   )
2268       DisplayFatalError(_("Recompile with BOARD_SIZE > 12, to support this size"), 0, 2);
2269
2270 #if !HIGHDRAG
2271     /* This feature does not work; animation needs a rewrite */
2272     appData.highlightDragging = FALSE;
2273 #endif
2274     InitBackEnd1();
2275
2276     xDisplay = XtDisplay(shellWidget);
2277     xScreen = DefaultScreen(xDisplay);
2278     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2279
2280     gameInfo.variant = StringToVariant(appData.variant);
2281     InitPosition(FALSE);
2282
2283     /* calc board size */
2284     if (isdigit(appData.boardSize[0])) 
2285       {
2286         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2287                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2288                    &fontPxlSize, &smallLayout, &tinyLayout);
2289         if (i == 0) 
2290           {
2291             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2292                     programName, appData.boardSize);
2293             exit(2);
2294           }
2295         if (i < 7) 
2296           {
2297             /* Find some defaults; use the nearest known size */
2298             SizeDefaults *szd, *nearest;
2299             int distance = 99999;
2300             nearest = szd = sizeDefaults;
2301             while (szd->name != NULL) 
2302               {
2303                 if (abs(szd->squareSize - squareSize) < distance) 
2304                   {
2305                     nearest = szd;
2306                     distance = abs(szd->squareSize - squareSize);
2307                     if (distance == 0) break;
2308                   }
2309                 szd++;
2310               };
2311             if (i < 2) lineGap = nearest->lineGap;
2312             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2313             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2314             if (i < 5) fontPxlSize = nearest->fontPxlSize;
2315             if (i < 6) smallLayout = nearest->smallLayout;
2316             if (i < 7) tinyLayout = nearest->tinyLayout;
2317           }
2318       } 
2319     else 
2320       {
2321         SizeDefaults *szd = sizeDefaults;
2322         if (*appData.boardSize == NULLCHAR) 
2323           {
2324             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize 
2325                    || DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) 
2326               {
2327                 szd++;
2328               }
2329             if (szd->name == NULL) szd--;
2330           } 
2331         else 
2332           {
2333             while (szd->name != NULL 
2334                    && StrCaseCmp(szd->name, appData.boardSize) != 0) 
2335               szd++;
2336             if (szd->name == NULL) 
2337               {
2338                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2339                         programName, appData.boardSize);
2340                 exit(2);
2341               }
2342           }
2343         squareSize = szd->squareSize;
2344         lineGap = szd->lineGap;
2345         clockFontPxlSize = szd->clockFontPxlSize;
2346         coordFontPxlSize = szd->coordFontPxlSize;
2347         fontPxlSize = szd->fontPxlSize;
2348         smallLayout = szd->smallLayout;
2349         tinyLayout = szd->tinyLayout;
2350       }
2351     /* end figuring out what size to use */
2352     
2353     boardWidth  = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2354     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2355     
2356     /*
2357      * Determine what fonts to use.
2358      */
2359     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2360     clockFontID = XLoadFont(xDisplay, appData.clockFont);
2361     clockFontStruct = XQueryFont(xDisplay, clockFontID);
2362     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2363     coordFontID = XLoadFont(xDisplay, appData.coordFont);
2364     coordFontStruct = XQueryFont(xDisplay, coordFontID);
2365     appData.font = FindFont(appData.font, fontPxlSize);
2366     countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
2367     countFontStruct = XQueryFont(xDisplay, countFontID);
2368 //    appData.font = FindFont(appData.font, fontPxlSize);
2369
2370     xdb = XtDatabase(xDisplay);
2371     XrmPutStringResource(&xdb, "*font", appData.font);
2372
2373     /*
2374      * Detect if there are not enough colors available and adapt.
2375      */
2376     if (DefaultDepth(xDisplay, xScreen) <= 2) {
2377       appData.monoMode = True;
2378     }
2379
2380     if (!appData.monoMode) {
2381         vFrom.addr = (caddr_t) appData.lightSquareColor;
2382         vFrom.size = strlen(appData.lightSquareColor);
2383         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2384         if (vTo.addr == NULL) {
2385           appData.monoMode = True;
2386           forceMono = True;
2387         } else {
2388           lightSquareColor = *(Pixel *) vTo.addr;
2389         }
2390     }
2391     if (!appData.monoMode) {
2392         vFrom.addr = (caddr_t) appData.darkSquareColor;
2393         vFrom.size = strlen(appData.darkSquareColor);
2394         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2395         if (vTo.addr == NULL) {
2396           appData.monoMode = True;
2397           forceMono = True;
2398         } else {
2399           darkSquareColor = *(Pixel *) vTo.addr;
2400         }
2401     }
2402     if (!appData.monoMode) {
2403         vFrom.addr = (caddr_t) appData.whitePieceColor;
2404         vFrom.size = strlen(appData.whitePieceColor);
2405         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2406         if (vTo.addr == NULL) {
2407           appData.monoMode = True;
2408           forceMono = True;
2409         } else {
2410           whitePieceColor = *(Pixel *) vTo.addr;
2411         }
2412     }
2413     if (!appData.monoMode) {
2414         vFrom.addr = (caddr_t) appData.blackPieceColor;
2415         vFrom.size = strlen(appData.blackPieceColor);
2416         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2417         if (vTo.addr == NULL) {
2418           appData.monoMode = True;
2419           forceMono = True;
2420         } else {
2421           blackPieceColor = *(Pixel *) vTo.addr;
2422         }
2423     }
2424
2425     if (!appData.monoMode) {
2426         vFrom.addr = (caddr_t) appData.highlightSquareColor;
2427         vFrom.size = strlen(appData.highlightSquareColor);
2428         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2429         if (vTo.addr == NULL) {
2430           appData.monoMode = True;
2431           forceMono = True;
2432         } else {
2433           highlightSquareColor = *(Pixel *) vTo.addr;
2434         }
2435     }
2436
2437     if (!appData.monoMode) {
2438         vFrom.addr = (caddr_t) appData.premoveHighlightColor;
2439         vFrom.size = strlen(appData.premoveHighlightColor);
2440         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2441         if (vTo.addr == NULL) {
2442           appData.monoMode = True;
2443           forceMono = True;
2444         } else {
2445           premoveHighlightColor = *(Pixel *) vTo.addr;
2446         }
2447     }
2448
2449     if (forceMono) {
2450       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2451               programName);
2452
2453       if (appData.bitmapDirectory == NULL ||
2454               appData.bitmapDirectory[0] == NULLCHAR)
2455             appData.bitmapDirectory = DEF_BITMAP_DIR;
2456     }
2457
2458     if (appData.lowTimeWarning && !appData.monoMode) {
2459       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2460       vFrom.size = strlen(appData.lowTimeWarningColor);
2461       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2462       if (vTo.addr == NULL)
2463                 appData.monoMode = True;
2464       else
2465                 lowTimeWarningColor = *(Pixel *) vTo.addr;
2466     }
2467
2468     if (appData.monoMode && appData.debugMode) {
2469         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2470                 (unsigned long) XWhitePixel(xDisplay, xScreen),
2471                 (unsigned long) XBlackPixel(xDisplay, xScreen));
2472     }
2473
2474     if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
2475         parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
2476         parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
2477         parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
2478         parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
2479         parse_cpair(ColorTell, appData.colorTell) < 0 ||
2480         parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
2481         parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
2482         parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
2483         parse_cpair(ColorNormal, appData.colorNormal) < 0)
2484       {
2485           if (appData.colorize) {
2486               fprintf(stderr,
2487                       _("%s: can't parse color names; disabling colorization\n"),
2488                       programName);
2489           }
2490           appData.colorize = FALSE;
2491       }
2492     textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2493     textColors[ColorNone].attr = 0;
2494
2495     //    XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2496
2497     /*
2498      * widget hierarchy
2499      */
2500     if (tinyLayout) {
2501         layoutName = "tinyLayout";
2502     } else if (smallLayout) {
2503         layoutName = "smallLayout";
2504     } else {
2505         layoutName = "normalLayout";
2506     }
2507
2508     if (appData.titleInWindow) {
2509       /* todo check what this appdata does */
2510     }
2511
2512     if (appData.showButtonBar) {
2513       /* TODO hide button bar if requested */
2514     }
2515
2516
2517     if (appData.titleInWindow)
2518       {
2519         if (smallLayout)
2520           {
2521             /* make it small */
2522             if (appData.showButtonBar)
2523               {
2524
2525               }
2526           }
2527         else
2528           {
2529             if (appData.showButtonBar)
2530               {
2531               }
2532           }
2533       }
2534     else
2535       {
2536       }
2537
2538
2539     /* set some checkboxes in the menu according to appData */
2540
2541     if (appData.alwaysPromoteToQueen)
2542       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Always Queen")),TRUE);
2543
2544     if (appData.animateDragging)
2545       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Dragging")),TRUE);
2546
2547     if (appData.animate)
2548       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Moving")),TRUE);
2549
2550     if (appData.autoComment)
2551       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Comment")),TRUE);
2552
2553     if (appData.autoCallFlag)
2554       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flag")),TRUE);
2555
2556     if (appData.autoFlipView)
2557       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flip View")),TRUE);
2558
2559     if (appData.autoObserve)
2560       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Observe")),TRUE);
2561
2562     if (appData.autoRaiseBoard)
2563       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Raise Board")),TRUE);
2564
2565     if (appData.autoSaveGames)
2566       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
2567
2568     if (appData.saveGameFile[0] != NULLCHAR)
2569       {
2570         /* Can't turn this off from menu */
2571         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
2572         gtk_action_set_sensitive(GTK_ACTION (gtk_builder_get_object (builder, "menuOptions.Auto Save")),FALSE);
2573       }
2574
2575     if (appData.blindfold)
2576       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Blindfold")),TRUE);
2577
2578     if (appData.flashCount > 0)
2579       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Flash Moves")),TRUE);
2580
2581     if (appData.getMoveList)
2582       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Get Move List")),TRUE);
2583
2584 #if HIGHDRAG
2585     if (appData.highlightDragging)
2586       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Dragging")),TRUE);
2587 #endif
2588
2589     if (appData.highlightLastMove)
2590       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Last Move")),TRUE);
2591
2592     if (appData.icsAlarm)
2593       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.ICS Alarm")),TRUE);
2594
2595     if (appData.ringBellAfterMoves)
2596       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Move Sound")),TRUE);
2597
2598     if (appData.oldSaveStyle)
2599       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Old Save Style")),TRUE);
2600
2601     if (appData.periodicUpdates)
2602       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Periodic Updates")),TRUE);
2603
2604     if (appData.ponderNextMove)
2605       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Ponder Next Move")),TRUE);
2606
2607     if (appData.popupExitMessage)
2608       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Exit Message")),TRUE);
2609
2610     if (appData.popupMoveErrors)
2611       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Move Errors")),TRUE);
2612
2613     if (appData.premove)
2614       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Premove")),TRUE);
2615
2616     if (appData.quietPlay)
2617       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Quit Play")),TRUE);
2618
2619     if (appData.showCoords)
2620       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Show Coords")),TRUE);
2621
2622     if (appData.showThinking)
2623       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Show Thinking")),TRUE);
2624
2625     if (appData.testLegality)
2626       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Test Legality")),TRUE);
2627
2628     /* end setting check boxes */
2629
2630     /* load square colors */
2631     SVGLightSquare   = load_pixbuf("svg/LightSquare.svg",squareSize);
2632     SVGDarkSquare    = load_pixbuf("svg/DarkSquare.svg",squareSize);
2633     SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2634
2635     /* use two icons to indicate if it is white's or black's turn */
2636     WhiteIcon  = load_pixbuf("svg/icon_white.svg",0);
2637     BlackIcon  = load_pixbuf("svg/icon_black.svg",0);
2638     WindowIcon = WhiteIcon;
2639     gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
2640
2641
2642     /* realize window */
2643     gtk_widget_show (GUI_Window);
2644
2645     /* recalc boardsize */
2646     CreateGCs();
2647     CreatePieces();
2648     CreatePieceMenus();
2649
2650     if (appData.animate || appData.animateDragging)
2651       CreateAnimVars();
2652
2653     InitBackEnd2();
2654
2655     if (errorExitStatus == -1) {
2656         if (appData.icsActive) {
2657             /* We now wait until we see "login:" from the ICS before
2658                sending the logon script (problems with timestamp otherwise) */
2659             /*ICSInitScript();*/
2660             if (appData.icsInputBox) ICSInputBoxPopUp();
2661         }
2662
2663         signal(SIGINT, IntSigHandler);
2664         signal(SIGTERM, IntSigHandler);
2665         if (*appData.cmailGameName != NULLCHAR) {
2666             signal(SIGUSR1, CmailSigHandler);
2667         }
2668     }
2669     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2670     InitPosition(TRUE);
2671
2672     /*
2673      * Create a cursor for the board widget.
2674      * (This needs to be called after the window has been created to have access to board-window)
2675      */
2676
2677     BoardCursor = gdk_cursor_new(GDK_HAND2);
2678     gdk_window_set_cursor(GUI_Board->window, BoardCursor);
2679     gdk_cursor_destroy(BoardCursor);
2680
2681     /* end cursor */
2682     gtk_main ();
2683
2684     if (appData.debugMode) fclose(debugFP); // [DM] debug
2685     return 0;
2686 }
2687
2688 void
2689 ShutDownFrontEnd()
2690 {
2691     if (appData.icsActive && oldICSInteractionTitle != NULL) {
2692         DisplayIcsInteractionTitle(oldICSInteractionTitle);
2693     }
2694     unlink(gameCopyFilename);
2695     unlink(gamePasteFilename);
2696 }
2697
2698 RETSIGTYPE TermSizeSigHandler(int sig)
2699 {
2700     update_ics_width();
2701 }
2702
2703 RETSIGTYPE
2704 IntSigHandler(sig)
2705      int sig;
2706 {
2707     ExitEvent(sig);
2708 }
2709
2710 RETSIGTYPE
2711 CmailSigHandler(sig)
2712      int sig;
2713 {
2714     int dummy = 0;
2715     int error;
2716
2717     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
2718
2719     /* Activate call-back function CmailSigHandlerCallBack()             */
2720     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2721
2722     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2723 }
2724
2725 void
2726 CmailSigHandlerCallBack(isr, closure, message, count, error)
2727      InputSourceRef isr;
2728      VOIDSTAR closure;
2729      char *message;
2730      int count;
2731      int error;
2732 {
2733     BoardToTop();
2734     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
2735 }
2736 /**** end signal code ****/
2737
2738
2739 void
2740 ICSInitScript()
2741 {
2742     FILE *f;
2743     char buf[MSG_SIZ];
2744     char *p;
2745
2746     f = fopen(appData.icsLogon, "r");
2747     if (f == NULL) {
2748         p = getenv("HOME");
2749         if (p != NULL) {
2750             strcpy(buf, p);
2751             strcat(buf, "/");
2752             strcat(buf, appData.icsLogon);
2753             f = fopen(buf, "r");
2754         }
2755     }
2756     if (f != NULL)
2757       ProcessICSInitScript(f);
2758 }
2759
2760 void
2761 ResetFrontEnd()
2762 {
2763     CommentPopDown();
2764     EditCommentPopDown();
2765     TagsPopDown();
2766     return;
2767 }
2768
2769 void
2770 SetMenuEnables(enab)
2771      Enables *enab;
2772 {
2773   GObject *o;
2774
2775   if (!builder) return;
2776   while (enab->name != NULL) {
2777     o = gtk_builder_get_object(builder, enab->name);
2778     if(GTK_IS_WIDGET(o))
2779       gtk_widget_set_sensitive(GTK_WIDGET (o),enab->value);
2780     else
2781       {
2782         if(GTK_IS_ACTION(o))
2783           gtk_action_set_sensitive(GTK_ACTION (o),enab->value);
2784         else
2785           DisplayError(enab->name, 0);
2786       }
2787     enab++;
2788   }
2789 }
2790
2791 void SetICSMode()
2792 {
2793   SetMenuEnables(icsEnables);
2794
2795 #ifdef ZIPPY
2796   if (appData.zippyPlay && !appData.noChessProgram)   /* [DM] icsEngineAnalyze */
2797     {}; //     XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2798 #endif
2799 }
2800
2801 void
2802 SetNCPMode()
2803 {
2804   SetMenuEnables(ncpEnables);
2805 }
2806
2807 void
2808 SetGNUMode()
2809 {
2810   SetMenuEnables(gnuEnables);
2811 }
2812
2813 void
2814 SetCmailMode()
2815 {
2816   SetMenuEnables(cmailEnables);
2817 }
2818
2819 void
2820 SetTrainingModeOn()
2821 {
2822   SetMenuEnables(trainingOnEnables);
2823   if (appData.showButtonBar) {
2824     //    XtSetSensitive(buttonBarWidget, False);
2825   }
2826   CommentPopDown();
2827 }
2828
2829 void
2830 SetTrainingModeOff()
2831 {
2832   SetMenuEnables(trainingOffEnables);
2833   if (appData.showButtonBar) {
2834     //    XtSetSensitive(buttonBarWidget, True);
2835   }
2836 }
2837
2838 void
2839 SetUserThinkingEnables()
2840 {
2841   if (appData.noChessProgram) return;
2842   SetMenuEnables(userThinkingEnables);
2843 }
2844
2845 void
2846 SetMachineThinkingEnables()
2847 {
2848   if (appData.noChessProgram) return;
2849   SetMenuEnables(machineThinkingEnables);
2850   switch (gameMode) {
2851   case MachinePlaysBlack:
2852   case MachinePlaysWhite:
2853   case TwoMachinesPlay:
2854 //    XtSetSensitive(XtNameToWidget(menuBarWidget,
2855 //                                ModeToWidgetName(gameMode)), True);
2856     break;
2857   default:
2858     break;
2859   }
2860 }
2861
2862 #define Abs(n) ((n)<0 ? -(n) : (n))
2863
2864 /*
2865  * Find a font that matches "pattern" that is as close as
2866  * possible to the targetPxlSize.  Prefer fonts that are k
2867  * pixels smaller to fonts that are k pixels larger.  The
2868  * pattern must be in the X Consortium standard format,
2869  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2870  * The return value should be freed with XtFree when no
2871  * longer needed.
2872  */
2873 char *FindFont(pattern, targetPxlSize)
2874      char *pattern;
2875      int targetPxlSize;
2876 {
2877     char **fonts, *p, *best, *scalable, *scalableTail;
2878     int i, j, nfonts, minerr, err, pxlSize;
2879
2880 #ifdef ENABLE_NLS
2881     char **missing_list;
2882     int missing_count;
2883     char *def_string, *base_fnt_lst, strInt[3];
2884     XFontSet fntSet;
2885     XFontStruct **fnt_list;
2886
2887     base_fnt_lst = calloc(1, strlen(pattern) + 3);
2888     sprintf(strInt, "%d", targetPxlSize);
2889     p = strstr(pattern, "--");
2890     strncpy(base_fnt_lst, pattern, p - pattern + 2);
2891     strcat(base_fnt_lst, strInt);
2892     strcat(base_fnt_lst, strchr(p + 2, '-'));
2893
2894     if ((fntSet = XCreateFontSet(xDisplay,
2895                                  base_fnt_lst,
2896                                  &missing_list,
2897                                  &missing_count,
2898                                  &def_string)) == NULL) {
2899
2900        fprintf(stderr, _("Unable to create font set.\n"));
2901        exit (2);
2902     }
2903
2904     nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
2905 #else
2906     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2907     if (nfonts < 1) {
2908         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2909                 programName, pattern);
2910         exit(2);
2911     }
2912 #endif
2913
2914     best = fonts[0];
2915     scalable = NULL;
2916     minerr = 999999;
2917     for (i=0; i<nfonts; i++) {
2918         j = 0;
2919         p = fonts[i];
2920         if (*p != '-') continue;
2921         while (j < 7) {
2922             if (*p == NULLCHAR) break;
2923             if (*p++ == '-') j++;
2924         }
2925         if (j < 7) continue;
2926         pxlSize = atoi(p);
2927         if (pxlSize == 0) {
2928             scalable = fonts[i];
2929             scalableTail = p;
2930         } else {
2931             err = pxlSize - targetPxlSize;
2932             if (Abs(err) < Abs(minerr) ||
2933                 (minerr > 0 && err < 0 && -err == minerr)) {
2934                 best = fonts[i];
2935                 minerr = err;
2936             }
2937         }
2938     }
2939     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2940         /* If the error is too big and there is a scalable font,
2941            use the scalable font. */
2942         int headlen = scalableTail - scalable;
2943         p = (char *) XtMalloc(strlen(scalable) + 10);
2944         while (isdigit(*scalableTail)) scalableTail++;
2945         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2946     } else {
2947         p = (char *) XtMalloc(strlen(best) + 1);
2948         strcpy(p, best);
2949     }
2950     if (appData.debugMode) {
2951         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
2952                 pattern, targetPxlSize, p);
2953     }
2954 #ifdef ENABLE_NLS
2955     if (missing_count > 0)
2956        XFreeStringList(missing_list);
2957     XFreeFontSet(xDisplay, fntSet);
2958 #else
2959      XFreeFontNames(fonts);
2960 #endif
2961     return p;
2962 }
2963
2964 void CreateGCs()
2965 {
2966   /* 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*/
2967
2968     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2969       | GCBackground | GCFunction | GCPlaneMask;
2970     XGCValues gc_values;
2971     GC copyInvertedGC;
2972
2973     gc_values.plane_mask = AllPlanes;
2974     gc_values.line_width = lineGap;
2975     gc_values.line_style = LineSolid;
2976     gc_values.function = GXcopy;
2977
2978     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2979     gc_values.background = XWhitePixel(xDisplay, xScreen);
2980     coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
2981     XSetFont(xDisplay, coordGC, coordFontID);
2982
2983     if (appData.monoMode) {
2984         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2985         gc_values.background = XBlackPixel(xDisplay, xScreen);
2986         lightSquareGC = wbPieceGC
2987           = XtGetGC(shellWidget, value_mask, &gc_values);
2988
2989         gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2990         gc_values.background = XWhitePixel(xDisplay, xScreen);
2991         darkSquareGC = bwPieceGC
2992           = XtGetGC(shellWidget, value_mask, &gc_values);
2993
2994         if (DefaultDepth(xDisplay, xScreen) == 1) {
2995             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2996             gc_values.function = GXcopyInverted;
2997             copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
2998             gc_values.function = GXcopy;
2999             if (XBlackPixel(xDisplay, xScreen) == 1) {
3000                 bwPieceGC = darkSquareGC;
3001                 wbPieceGC = copyInvertedGC;
3002             } else {
3003                 bwPieceGC = copyInvertedGC;
3004                 wbPieceGC = lightSquareGC;
3005             }
3006         }
3007     } else {
3008         gc_values.foreground = lightSquareColor;
3009         gc_values.background = darkSquareColor;
3010         lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3011
3012         gc_values.foreground = darkSquareColor;
3013         gc_values.background = lightSquareColor;
3014         darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3015
3016         gc_values.foreground = jailSquareColor;
3017         gc_values.background = jailSquareColor;
3018         jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3019
3020         gc_values.foreground = whitePieceColor;
3021         gc_values.background = darkSquareColor;
3022         wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3023
3024         gc_values.foreground = whitePieceColor;
3025         gc_values.background = lightSquareColor;
3026         wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3027
3028         gc_values.foreground = whitePieceColor;
3029         gc_values.background = jailSquareColor;
3030         wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3031
3032         gc_values.foreground = blackPieceColor;
3033         gc_values.background = darkSquareColor;
3034         bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3035
3036         gc_values.foreground = blackPieceColor;
3037         gc_values.background = lightSquareColor;
3038         blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3039
3040         gc_values.foreground = blackPieceColor;
3041         gc_values.background = jailSquareColor;
3042         bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3043     }
3044 }
3045
3046 void CreatePieces()
3047 {
3048   int i;
3049
3050   /* free if used 
3051   for(i=0;i<MAXPIECES;i++)
3052     {
3053       if(SVGpieces[i])
3054         {       
3055           g_free(SVGpieces[i]);
3056           SVGpieces[i]=NULL;
3057         }
3058     }
3059   */
3060
3061   /* reload these */
3062   SVGLightSquare   = load_pixbuf("svg/LightSquare.svg",squareSize);
3063   SVGDarkSquare    = load_pixbuf("svg/DarkSquare.svg",squareSize);
3064   SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
3065
3066
3067   /* get some defaults going */
3068   for(i=WhitePawn; i<DemotePiece+1; i++)
3069     SVGpieces[i]   = load_pixbuf("svg/NeutralSquare.svg",squareSize);
3070     
3071   SVGpieces[WhitePawn]   = load_pixbuf("svg/WhitePawn.svg",squareSize);
3072   SVGpieces[WhiteKnight] = load_pixbuf("svg/WhiteKnight.svg",squareSize);
3073   SVGpieces[WhiteBishop] = load_pixbuf("svg/WhiteBishop.svg",squareSize);
3074   SVGpieces[WhiteRook]   = load_pixbuf("svg/WhiteRook.svg",squareSize);
3075   SVGpieces[WhiteQueen]  = load_pixbuf("svg/WhiteQueen.svg",squareSize);
3076   SVGpieces[WhiteKing]   = load_pixbuf("svg/WhiteKing.svg",squareSize);
3077
3078   SVGpieces[BlackPawn]   = load_pixbuf("svg/BlackPawn.svg",squareSize);
3079   SVGpieces[BlackKnight] = load_pixbuf("svg/BlackKnight.svg",squareSize);
3080   SVGpieces[BlackBishop] = load_pixbuf("svg/BlackBishop.svg",squareSize);
3081   SVGpieces[BlackRook]   = load_pixbuf("svg/BlackRook.svg",squareSize);
3082   SVGpieces[BlackQueen]  = load_pixbuf("svg/BlackQueen.svg",squareSize);
3083   SVGpieces[BlackKing]   = load_pixbuf("svg/BlackKing.svg",squareSize);
3084
3085   return;
3086 }
3087
3088
3089 static void MenuBarSelect(w, addr, index)
3090      Widget w;
3091      caddr_t addr;
3092      caddr_t index;
3093 {
3094     XtActionProc proc = (XtActionProc) addr;
3095
3096     (proc)(NULL, NULL, NULL, NULL);
3097 }
3098
3099 void CreateMenuBarPopup(parent, name, mb)
3100      Widget parent;
3101      String name;
3102      Menu *mb;
3103 {
3104     int j;
3105     Widget menu, entry;
3106     MenuItem *mi;
3107     Arg args[16];
3108
3109     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3110                               parent, NULL, 0);
3111     j = 0;
3112     XtSetArg(args[j], XtNleftMargin, 20);   j++;
3113     XtSetArg(args[j], XtNrightMargin, 20);  j++;
3114     mi = mb->mi;
3115     while (mi->string != NULL) {
3116         if (strcmp(mi->string, "----") == 0) {
3117             entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3118                                           menu, args, j);
3119         } else {
3120           XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3121             entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
3122                                           menu, args, j+1);
3123             XtAddCallback(entry, XtNcallback,
3124                           (XtCallbackProc) MenuBarSelect,
3125                           (caddr_t) mi->proc);
3126         }
3127         mi++;
3128     }
3129 }
3130
3131 Widget CreateMenuBar(mb)
3132      Menu *mb;
3133 {
3134     int j;
3135     Widget anchor, menuBar;
3136     Arg args[16];
3137     char menuName[MSG_SIZ];
3138
3139     j = 0;
3140     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
3141     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3142     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3143     menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3144                              formWidget, args, j);
3145
3146     while (mb->name != NULL) {
3147         strcpy(menuName, "menu");
3148         strcat(menuName, mb->name);
3149         j = 0;
3150         XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
3151         if (tinyLayout) {
3152             char shortName[2];
3153             shortName[0] = _(mb->name)[0];
3154             shortName[1] = NULLCHAR;
3155             XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3156         }
3157       else {
3158           XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3159       }
3160
3161         XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3162         anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3163                                        menuBar, args, j);
3164         CreateMenuBarPopup(menuBar, menuName, mb);
3165         mb++;
3166     }
3167     return menuBar;
3168 }
3169
3170
3171 Widget
3172 CreatePieceMenu(name, color)
3173      char *name;
3174      int color;
3175 {
3176     int i;
3177     Widget entry, menu;
3178     Arg args[16];
3179     ChessSquare selection;
3180
3181     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3182                               boardWidget, args, 0);
3183
3184     for (i = 0; i < PIECE_MENU_SIZE; i++) {
3185         String item = pieceMenuStrings[color][i];
3186
3187         if (strcmp(item, "----") == 0) {
3188             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3189                                           menu, NULL, 0);
3190         } else {
3191           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3192             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3193                                 menu, args, 1);
3194             selection = pieceMenuTranslation[color][i];
3195             XtAddCallback(entry, XtNcallback,
3196                           (XtCallbackProc) PieceMenuSelect,
3197                           (caddr_t) selection);
3198             if (selection == WhitePawn || selection == BlackPawn) {
3199                 XtSetArg(args[0], XtNpopupOnEntry, entry);
3200                 XtSetValues(menu, args, 1);
3201             }
3202         }
3203     }
3204     return menu;
3205 }
3206
3207 void
3208 CreatePieceMenus()
3209 {
3210     int i;
3211     Widget entry;
3212     Arg args[16];
3213     ChessSquare selection;
3214
3215 //    whitePieceMenu = CreatePieceMenu("menuW", 0);
3216 //    blackPieceMenu = CreatePieceMenu("menuB", 1);
3217 //
3218 //    XtRegisterGrabAction(PieceMenuPopup, True,
3219 //                       (unsigned)(ButtonPressMask|ButtonReleaseMask),
3220 //                       GrabModeAsync, GrabModeAsync);
3221 //
3222 //    XtSetArg(args[0], XtNlabel, _("Drop"));
3223 //    dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3224 //                                boardWidget, args, 1);
3225 //    for (i = 0; i < DROP_MENU_SIZE; i++) {
3226 //      String item = dropMenuStrings[i];
3227 //
3228 //      if (strcmp(item, "----") == 0) {
3229 //          entry = XtCreateManagedWidget(item, smeLineObjectClass,
3230 //                                        dropMenu, NULL, 0);
3231 //      } else {
3232 //          XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3233 //          entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3234 //                                dropMenu, args, 1);
3235 //          selection = dropMenuTranslation[i];
3236 //          XtAddCallback(entry, XtNcallback,
3237 //                        (XtCallbackProc) DropMenuSelect,
3238 //                        (caddr_t) selection);
3239 //      }
3240 //    }
3241 }
3242
3243 void SetupDropMenu()
3244 {
3245     int i, j, count;
3246     char label[32];
3247     Arg args[16];
3248     Widget entry;
3249     char* p;
3250
3251     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3252         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3253         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3254                    dmEnables[i].piece);
3255         XtSetSensitive(entry, p != NULL || !appData.testLegality
3256                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3257                                        && !appData.icsActive));
3258         count = 0;
3259         while (p && *p++ == dmEnables[i].piece) count++;
3260         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
3261         j = 0;
3262         XtSetArg(args[j], XtNlabel, label); j++;
3263         XtSetValues(entry, args, j);
3264     }
3265 }
3266
3267 void PieceMenuPopup(w, event, params, num_params)
3268      Widget w;
3269      XEvent *event;
3270      String *params;
3271      Cardinal *num_params;
3272 {
3273     String whichMenu;
3274     if (event->type != ButtonPress) return;
3275     if (errorUp) ErrorPopDown();
3276     switch (gameMode) {
3277       case EditPosition:
3278       case IcsExamining:
3279         whichMenu = params[0];
3280         break;
3281       case IcsPlayingWhite:
3282       case IcsPlayingBlack:
3283       case EditGame:
3284       case MachinePlaysWhite:
3285       case MachinePlaysBlack:
3286         if (appData.testLegality &&
3287             gameInfo.variant != VariantBughouse &&
3288             gameInfo.variant != VariantCrazyhouse) return;
3289         SetupDropMenu();
3290         whichMenu = "menuD";
3291         break;
3292       default:
3293         return;
3294     }
3295
3296     if (((pmFromX = EventToSquare(event->xbutton.x, BOARD_WIDTH)) < 0) ||
3297         ((pmFromY = EventToSquare(event->xbutton.y, BOARD_HEIGHT)) < 0)) {
3298         pmFromX = pmFromY = -1;
3299         return;
3300     }
3301     if (flipView)
3302       pmFromX = BOARD_WIDTH - 1 - pmFromX;
3303     else
3304       pmFromY = BOARD_HEIGHT - 1 - pmFromY;
3305
3306     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3307 }
3308
3309 static void PieceMenuSelect(w, piece, junk)
3310      Widget w;
3311      ChessSquare piece;
3312      caddr_t junk;
3313 {
3314     if (pmFromX < 0 || pmFromY < 0) return;
3315     EditPositionMenuEvent(piece, pmFromX, pmFromY);
3316 }
3317
3318 static void DropMenuSelect(w, piece, junk)
3319      Widget w;
3320      ChessSquare piece;
3321      caddr_t junk;
3322 {
3323     if (pmFromX < 0 || pmFromY < 0) return;
3324     DropMenuEvent(piece, pmFromX, pmFromY);
3325 }
3326
3327 /*
3328  * If the user selects on a border boundary, return -1; if off the board,
3329  *   return -2.  Otherwise map the event coordinate to the square.
3330  */
3331 int EventToSquare(x, limit)
3332      int x;
3333 {
3334     if (x <= 0)
3335       return -2;
3336     if (x < lineGap)
3337       return -1;
3338     x -= lineGap;
3339     if ((x % (squareSize + lineGap)) >= squareSize)
3340       return -1;
3341     x /= (squareSize + lineGap);
3342     if (x >= limit)
3343       return -2;
3344     return x;
3345 }
3346
3347 static void do_flash_delay(msec)
3348      unsigned long msec;
3349 {
3350     TimeDelay(msec);
3351 }
3352
3353 static void drawHighlight(file, rank, line_type)
3354      int file, rank, line_type;
3355 {
3356     int x, y;
3357     cairo_t *cr;
3358
3359     if (lineGap == 0 || appData.blindfold) return;
3360
3361     if (flipView)
3362       {
3363         x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
3364           (squareSize + lineGap);
3365         y = lineGap/2 + rank * (squareSize + lineGap);
3366       }
3367     else
3368       {
3369         x = lineGap/2 + file * (squareSize + lineGap);
3370         y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
3371           (squareSize + lineGap);
3372       }
3373
3374     /* get a cairo_t */
3375     cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3376
3377     /* draw the highlight */
3378     cairo_move_to (cr, x, y);
3379     cairo_rel_line_to (cr, 0,squareSize+lineGap);
3380     cairo_rel_line_to (cr, squareSize+lineGap,0);
3381     cairo_rel_line_to (cr, 0,-squareSize-lineGap);
3382     cairo_close_path (cr);
3383
3384     cairo_set_line_width (cr, lineGap);
3385     switch(line_type)
3386       {
3387         /* TODO: use appdata colors */
3388       case LINE_TYPE_HIGHLIGHT:
3389         cairo_set_source_rgba (cr, 1, 1, 0, 1.0);
3390         break;
3391       case LINE_TYPE_PRE:
3392         cairo_set_source_rgba (cr, 1, 0, 0, 1.0);
3393         break;
3394       case LINE_TYPE_NORMAL:
3395       default:
3396         cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
3397       }
3398
3399     cairo_stroke (cr);
3400
3401     /* free memory */
3402     cairo_destroy (cr);
3403
3404     return;
3405 }
3406
3407 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
3408 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
3409
3410 void
3411 SetHighlights(fromX, fromY, toX, toY)
3412      int fromX, fromY, toX, toY;
3413 {
3414     if (hi1X != fromX || hi1Y != fromY)
3415       {
3416         if (hi1X >= 0 && hi1Y >= 0)
3417           {
3418             drawHighlight(hi1X, hi1Y, LINE_TYPE_NORMAL);
3419           }
3420         if (fromX >= 0 && fromY >= 0)
3421           {
3422             drawHighlight(fromX, fromY, LINE_TYPE_HIGHLIGHT);
3423           }
3424       }
3425     if (hi2X != toX || hi2Y != toY)
3426       {
3427         if (hi2X >= 0 && hi2Y >= 0)
3428           {
3429             drawHighlight(hi2X, hi2Y, LINE_TYPE_NORMAL);
3430           }
3431         if (toX >= 0 && toY >= 0)
3432           {
3433             drawHighlight(toX, toY, LINE_TYPE_HIGHLIGHT);
3434           }
3435       }
3436     hi1X = fromX;
3437     hi1Y = fromY;
3438     hi2X = toX;
3439     hi2Y = toY;
3440
3441     return;
3442 }
3443
3444 void
3445 ClearHighlights()
3446 {
3447     SetHighlights(-1, -1, -1, -1);
3448 }
3449
3450
3451 void
3452 SetPremoveHighlights(fromX, fromY, toX, toY)
3453      int fromX, fromY, toX, toY;
3454 {
3455     if (pm1X != fromX || pm1Y != fromY)
3456       {
3457         if (pm1X >= 0 && pm1Y >= 0)
3458           {
3459             drawHighlight(pm1X, pm1Y, LINE_TYPE_NORMAL);
3460           }
3461         if (fromX >= 0 && fromY >= 0)
3462           {
3463             drawHighlight(fromX, fromY, LINE_TYPE_PRE);
3464           }
3465       }
3466     if (pm2X != toX || pm2Y != toY)
3467       {
3468         if (pm2X >= 0 && pm2Y >= 0)
3469           {
3470             drawHighlight(pm2X, pm2Y, LINE_TYPE_NORMAL);
3471           }
3472         if (toX >= 0 && toY >= 0)
3473           {
3474             drawHighlight(toX, toY, LINE_TYPE_PRE);
3475           }
3476       }
3477
3478     pm1X = fromX;
3479     pm1Y = fromY;
3480     pm2X = toX;
3481     pm2Y = toY;
3482
3483     return;
3484 }
3485
3486 void
3487 ClearPremoveHighlights()
3488 {
3489   SetPremoveHighlights(-1, -1, -1, -1);
3490 }
3491
3492 static void BlankSquare(x, y, color, piece, dest)
3493      int x, y, color;
3494      ChessSquare piece;
3495      Drawable dest;
3496 {
3497       GdkPixbuf *pb;
3498
3499       switch (color) 
3500         {
3501         case 0: /* dark */
3502           pb = SVGDarkSquare;
3503           break;
3504         case 1: /* light */
3505           pb = SVGLightSquare;
3506           break;
3507         case 2: /* neutral */
3508         default:
3509           pb = SVGNeutralSquare;
3510           break;
3511         }
3512       gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,pb,0,0,x,y,-1,-1, GDK_RGB_DITHER_NORMAL, 0, 0);
3513       return;
3514 }
3515
3516 static void DrawPiece(piece, square_color, x, y, dest)
3517      ChessSquare piece;
3518      int square_color, x, y;
3519      Drawable dest;
3520 {
3521   /* redraw background, since piece might be transparent in some areas */
3522   BlankSquare(x,y,square_color,piece,dest);
3523
3524   /* draw piece */
3525   gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,
3526                   GDK_PIXBUF(SVGpieces[piece]),0,0,x,y,-1,-1,
3527                   GDK_RGB_DITHER_NORMAL, 0, 0);
3528   return ;
3529 }
3530
3531 /* [HR] determine square color depending on chess variant. */
3532 static int SquareColor(row, column)
3533      int row, column;
3534 {
3535     int square_color;
3536
3537     if (gameInfo.variant == VariantXiangqi) {
3538         if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
3539             square_color = 1;
3540         } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
3541             square_color = 0;
3542         } else if (row <= 4) {
3543             square_color = 0;
3544         } else {
3545             square_color = 1;
3546         }
3547     } else {
3548         square_color = ((column + row) % 2) == 1;
3549     }
3550
3551     /* [hgm] holdings: next line makes all holdings squares light */
3552     if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
3553
3554     return square_color;
3555 }
3556
3557 void DrawSquare(row, column, piece, do_flash)
3558      int row, column, do_flash;
3559      ChessSquare piece;
3560 {
3561     int square_color, x, y;
3562     int i;
3563     char string[2];
3564     int flash_delay;
3565
3566     /* Calculate delay in milliseconds (2-delays per complete flash) */
3567     flash_delay = 500 / appData.flashRate;
3568
3569     /* calculate x and y coordinates from row and column */
3570     if (flipView)
3571       {
3572         x = lineGap + ((BOARD_WIDTH-1)-column) *
3573           (squareSize + lineGap);
3574         y = lineGap + row * (squareSize + lineGap);
3575       }
3576     else
3577       {
3578         x = lineGap + column * (squareSize + lineGap);
3579         y = lineGap + ((BOARD_HEIGHT-1)-row) *
3580           (squareSize + lineGap);
3581       }
3582
3583     square_color = SquareColor(row, column);
3584
3585     // [HGM] holdings: blank out area between board and holdings
3586     if ( column == BOARD_LEFT-1 ||  column == BOARD_RGHT
3587          || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
3588          || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) )
3589       {
3590         BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
3591
3592         // [HGM] print piece counts next to holdings
3593         string[1] = NULLCHAR;
3594         if(piece > 1)
3595           {
3596             cairo_text_extents_t extents;
3597             cairo_t *cr;
3598             int  xpos, ypos;
3599
3600             /* get a cairo_t */
3601             cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3602
3603             string[0] = '0' + piece;
3604
3605             /* TODO this has to go into the font-selection */
3606             cairo_select_font_face (cr, "Sans",
3607                                     CAIRO_FONT_SLANT_NORMAL,
3608                                     CAIRO_FONT_WEIGHT_NORMAL);
3609
3610             cairo_set_font_size (cr, 12.0);
3611             cairo_text_extents (cr, string, &extents);
3612
3613             if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) )
3614               {
3615                 xpos= x + squareSize - extents.width - 2;
3616                 ypos= y + extents.y_bearing + 1;
3617               }
3618             if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1)
3619               {
3620                 xpos= x + 2;
3621                 ypos = y + extents.y_bearing + 1;
3622               }
3623
3624             /* TODO mono mode? */
3625             cairo_move_to (cr, xpos, ypos);
3626             cairo_text_path (cr, string);
3627             cairo_set_source_rgb (cr, 1.0, 1.0, 1);
3628             cairo_fill_preserve (cr);
3629             cairo_set_source_rgb (cr, 0, 0, 0);
3630             cairo_set_line_width (cr, 0.1);
3631             cairo_stroke (cr);
3632
3633             /* free memory */
3634             cairo_destroy (cr);
3635           }
3636       }
3637     else
3638       {
3639         /* square on the board */
3640         if (piece == EmptySquare || appData.blindfold)
3641           {
3642             BlankSquare(x, y, square_color, piece, xBoardWindow);
3643           }
3644         else
3645           {
3646             if (do_flash && appData.flashCount > 0)
3647               {
3648                 for (i=0; i<appData.flashCount; ++i)
3649                   {
3650
3651                     DrawPiece(piece, square_color, x, y, xBoardWindow);
3652                     do_flash_delay(flash_delay);
3653
3654                     BlankSquare(x, y, square_color, piece, xBoardWindow);
3655                     do_flash_delay(flash_delay);
3656                   }
3657               }
3658             DrawPiece(piece, square_color, x, y, xBoardWindow);
3659           }
3660       }
3661
3662     /* show coordinates if necessary */
3663     if(appData.showCoords)
3664       {
3665         cairo_text_extents_t extents;
3666         cairo_t *cr;
3667         int  xpos, ypos;
3668
3669         /* TODO this has to go into the font-selection */
3670         cairo_select_font_face (cr, "Sans",
3671                                 CAIRO_FONT_SLANT_NORMAL,
3672                                 CAIRO_FONT_WEIGHT_NORMAL);
3673         cairo_set_font_size (cr, 12.0);
3674
3675         string[1] = NULLCHAR;
3676
3677         /* get a cairo_t */
3678         cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3679
3680         if (row == (flipView ? BOARD_HEIGHT-1 : 0) &&
3681             column >= BOARD_LEFT && column < BOARD_RGHT)
3682           {
3683             string[0] = 'a' + column - BOARD_LEFT;
3684             cairo_text_extents (cr, string, &extents);
3685
3686             xpos = x + squareSize - extents.width - 2;
3687             ypos = y + squareSize - extents.height - extents.y_bearing - 1;
3688
3689             if (appData.monoMode)
3690               { /*TODO*/
3691               }
3692             else
3693               {
3694               }
3695
3696             cairo_move_to (cr, xpos, ypos);
3697             cairo_text_path (cr, string);
3698             cairo_set_source_rgb (cr, 0.0, 0.0, 0);
3699             cairo_fill_preserve (cr);
3700             cairo_set_source_rgb (cr, 0, 1.0, 0);
3701             cairo_set_line_width (cr, 0.1);
3702             cairo_stroke (cr);
3703           }
3704         if ( column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT))
3705           {
3706
3707             string[0] = ONE + row;
3708             cairo_text_extents (cr, string, &extents);
3709
3710             xpos = x + 2;
3711             ypos = y + extents.height + 1;
3712
3713             if (appData.monoMode)
3714               { /*TODO*/
3715               }
3716             else
3717               {
3718               }
3719
3720             cairo_move_to (cr, xpos, ypos);
3721             cairo_text_path (cr, string);
3722             cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
3723             cairo_fill_preserve (cr);
3724             cairo_set_source_rgb (cr, 0, 0, 1.0);
3725             cairo_set_line_width (cr, 0.1);
3726             cairo_stroke (cr);
3727
3728           }
3729         /* free memory */
3730         cairo_destroy (cr);
3731       }
3732
3733     return;
3734 }
3735
3736
3737 /* Returns 1 if there are "too many" differences between b1 and b2
3738    (i.e. more than 1 move was made) */
3739 static int too_many_diffs(b1, b2)
3740      Board b1, b2;
3741 {
3742     int i, j;
3743     int c = 0;
3744
3745     for (i=0; i<BOARD_HEIGHT; ++i) {
3746         for (j=0; j<BOARD_WIDTH; ++j) {
3747             if (b1[i][j] != b2[i][j]) {
3748                 if (++c > 4)    /* Castling causes 4 diffs */
3749                   return 1;
3750             }
3751         }
3752     }
3753
3754     return 0;
3755 }
3756
3757 /* Matrix describing castling maneuvers */
3758 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
3759 static int castling_matrix[4][5] = {
3760     { 0, 0, 4, 3, 2 },          /* 0-0-0, white */
3761     { 0, 7, 4, 5, 6 },          /* 0-0,   white */
3762     { 7, 0, 4, 3, 2 },          /* 0-0-0, black */
3763     { 7, 7, 4, 5, 6 }           /* 0-0,   black */
3764 };
3765
3766 /* Checks whether castling occurred. If it did, *rrow and *rcol
3767    are set to the destination (row,col) of the rook that moved.
3768
3769    Returns 1 if castling occurred, 0 if not.
3770
3771    Note: Only handles a max of 1 castling move, so be sure
3772    to call too_many_diffs() first.
3773    */
3774 static int check_castle_draw(newb, oldb, rrow, rcol)
3775      Board newb, oldb;
3776      int *rrow, *rcol;
3777 {
3778     int i, *r, j;
3779     int match;
3780
3781     /* For each type of castling... */
3782     for (i=0; i<4; ++i) {
3783         r = castling_matrix[i];
3784
3785         /* Check the 4 squares involved in the castling move */
3786         match = 0;
3787         for (j=1; j<=4; ++j) {
3788             if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
3789                 match = 1;
3790                 break;
3791             }
3792         }
3793
3794         if (!match) {
3795             /* All 4 changed, so it must be a castling move */
3796             *rrow = r[0];
3797             *rcol = r[3];
3798             return 1;
3799         }
3800     }
3801     return 0;
3802 }
3803
3804 static int damage[BOARD_SIZE][BOARD_SIZE];
3805
3806 /*
3807  * event handler for redrawing the board
3808  */
3809 void DrawPosition( repaint, board)
3810      /*Boolean*/int repaint;
3811                 Board board;
3812 {
3813   int i, j, do_flash;
3814   static int lastFlipView = 0;
3815   static int lastBoardValid = 0;
3816   static Board lastBoard;
3817   int rrow, rcol;
3818
3819   if (board == NULL) {
3820     if (!lastBoardValid) return;
3821     board = lastBoard;
3822   }
3823   if (!lastBoardValid || lastFlipView != flipView) {
3824     //    XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
3825     // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
3826     //  args, 1);
3827   }
3828
3829   /*
3830    * It would be simpler to clear the window with XClearWindow()
3831    * but this causes a very distracting flicker.
3832    */
3833
3834   if (!repaint && lastBoardValid && lastFlipView == flipView)
3835     {
3836       /* If too much changes (begin observing new game, etc.), don't
3837          do flashing */
3838       do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
3839
3840       /* Special check for castling so we don't flash both the king
3841          and the rook (just flash the king). */
3842       if (do_flash)
3843         {
3844           if (check_castle_draw(board, lastBoard, &rrow, &rcol))
3845             {
3846               /* Draw rook with NO flashing. King will be drawn flashing later */
3847               DrawSquare(rrow, rcol, board[rrow][rcol], 0);
3848               lastBoard[rrow][rcol] = board[rrow][rcol];
3849             }
3850         }
3851
3852       /* First pass -- Draw (newly) empty squares and repair damage.
3853          This prevents you from having a piece show up twice while it
3854          is flashing on its new square */
3855       for (i = 0; i < BOARD_HEIGHT; i++)
3856         for (j = 0; j < BOARD_WIDTH; j++)
3857           if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
3858               || damage[i][j])
3859             {
3860               DrawSquare(i, j, board[i][j], 0);
3861               damage[i][j] = False;
3862             }
3863
3864       /* Second pass -- Draw piece(s) in new position and flash them */
3865       for (i = 0; i < BOARD_HEIGHT; i++)
3866         for (j = 0; j < BOARD_WIDTH; j++)
3867           if (board[i][j] != lastBoard[i][j])
3868             {
3869               DrawSquare(i, j, board[i][j], do_flash);
3870             }
3871     }
3872   else
3873     {
3874       /* redraw Grid */
3875       if (lineGap > 0)
3876         {
3877           int x1,x2,y1,y2;
3878           cairo_t *cr;
3879
3880           /* get a cairo_t */
3881           cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3882
3883           cairo_set_line_width (cr, lineGap);
3884
3885           /* TODO: use appdata colors */
3886           cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
3887
3888           cairo_stroke (cr);
3889
3890           for (i = 0; i < BOARD_HEIGHT + 1; i++)
3891             {
3892               x1 = 0;
3893               x2 = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3894               y1 = y2 = lineGap / 2 + (i * (squareSize + lineGap));
3895
3896               cairo_move_to (cr, x1, y1);
3897               cairo_rel_line_to (cr, x2,0);
3898               cairo_stroke (cr);
3899             }
3900
3901           for (j = 0; j < BOARD_WIDTH + 1; j++)
3902             {
3903               y1 = 0;
3904               y2 = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3905               x1 = x2  = lineGap / 2 + (j * (squareSize + lineGap));
3906
3907               cairo_move_to (cr, x1, y1);
3908               cairo_rel_line_to (cr, 0, y2);
3909               cairo_stroke (cr);
3910             }
3911
3912           /* free memory */
3913           cairo_destroy (cr);
3914         }
3915
3916       /* draw pieces */
3917       for (i = 0; i < BOARD_HEIGHT; i++)
3918         for (j = 0; j < BOARD_WIDTH; j++)
3919           {
3920             DrawSquare(i, j, board[i][j], 0);
3921             damage[i][j] = False;
3922           }
3923     }
3924
3925   CopyBoard(lastBoard, board);
3926   lastBoardValid = 1;
3927   lastFlipView = flipView;
3928
3929   /* Draw highlights */
3930   if (pm1X >= 0 && pm1Y >= 0)
3931     {
3932       drawHighlight(pm1X, pm1Y, LINE_TYPE_PRE);
3933     }
3934   if (pm2X >= 0 && pm2Y >= 0)
3935     {
3936       drawHighlight(pm2X, pm2Y, LINE_TYPE_PRE);
3937     }
3938   if (hi1X >= 0 && hi1Y >= 0)
3939     {
3940       drawHighlight(hi1X, hi1Y, LINE_TYPE_HIGHLIGHT);
3941     }
3942   if (hi2X >= 0 && hi2Y >= 0)
3943     {
3944       drawHighlight(hi2X, hi2Y, LINE_TYPE_HIGHLIGHT);
3945     }
3946
3947   /* If piece being dragged around board, must redraw that too */
3948   DrawDragPiece();
3949
3950   return;
3951 }
3952
3953 /*
3954  * event handler for parsing user moves
3955  */
3956 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3957 //       way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3958 //       it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3959 //       should be made to use the new way, of calling UserMoveTest early  to determine the legality of the
3960 //       move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3961 //       and at the end FinishMove() to perform the move after optional promotion popups.
3962 //       For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3963 void HandleUserMove(w, event, prms, nprms)
3964      Widget w;
3965      XEvent *event;
3966      String *prms;
3967      Cardinal *nprms;
3968 {
3969     int x, y;
3970     Boolean saveAnimate;
3971     static int second = 0, promotionChoice = 0;
3972     ChessMove moveType;
3973
3974     if (w != boardWidget || errorExitStatus != -1) return;
3975
3976     x = EventToSquare(event->xbutton.x, BOARD_WIDTH);
3977     y = EventToSquare(event->xbutton.y, BOARD_HEIGHT);
3978     if (!flipView && y >= 0) {
3979         y = BOARD_HEIGHT - 1 - y;
3980     }
3981     if (flipView && x >= 0) {
3982         x = BOARD_WIDTH - 1 - x;
3983     }
3984
3985     if(promotionChoice) { // we are waiting for a click to indicate promotion piece
3986         if(event->type == ButtonRelease) return; // ignore upclick of click-click destination
3987         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel
3988         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);
3989         if(gameInfo.holdingsWidth && 
3990                 (WhiteOnMove(currentMove) 
3991                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0
3992                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {
3993             // click in right holdings, for determining promotion piece
3994             ChessSquare p = boards[currentMove][y][x];
3995             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);
3996             if(p != EmptySquare) {
3997                 FinishMove(NormalMove, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));
3998                 fromX = fromY = -1;
3999                 return;
4000             }
4001         }
4002         DrawPosition(FALSE, boards[currentMove]);
4003         return;
4004     }
4005     if (event->type == ButtonPress) ErrorPopDown();
4006
4007     if (promotionUp) {
4008         if (event->type == ButtonPress) {
4009 //          XtPopdown(promotionShell);
4010 //          XtDestroyWidget(promotionShell);
4011             promotionUp = False;
4012             ClearHighlights();
4013             fromX = fromY = -1;
4014         } else {
4015             return;
4016         }
4017     }
4018
4019     /* [HGM] holdings: next 5 lines: ignore all clicks between board and holdings */
4020     if(event->type == ButtonPress
4021             && ( x == BOARD_LEFT-1 || 
4022                  x == BOARD_RGHT   || 
4023                  (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize ) || 
4024                  (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)) )
4025         return;
4026
4027     if (fromX == -1) {
4028         if (event->type == ButtonPress) {
4029             /* First square, prepare to drag */
4030             if (OKToStartUserMove(x, y)) {
4031                 fromX = x;
4032                 fromY = y;
4033                 second = 0;
4034                 DragPieceBegin(event->xbutton.x, event->xbutton.y);
4035                 if (appData.highlightDragging) {
4036                     SetHighlights(x, y, -1, -1);
4037                 }
4038             }
4039         }
4040         return;
4041     }
4042
4043     /* fromX != -1 */
4044     if (event->type == ButtonRelease && x == fromX && y == fromY) {
4045     /* Click on single square in stead of drag-drop */
4046         DragPieceEnd(event->xbutton.x, event->xbutton.y);
4047         if (appData.animateDragging) {
4048             /* Undo animation damage if any */
4049             DrawPosition(FALSE, NULL);
4050         }
4051         if (second) {
4052             /* Second up/down in same square; just abort move */
4053             second = 0;
4054             fromX = fromY = -1;
4055             ClearHighlights();
4056             gotPremove = 0;
4057             ClearPremoveHighlights();
4058         } else {
4059             /* First upclick in same square; start click-click mode */
4060             SetHighlights(x, y, -1, -1);
4061         }
4062         return;
4063     }
4064
4065     moveType = UserMoveTest(fromX, fromY, x, y, NULLCHAR, event->type == ButtonRelease);
4066
4067     if (moveType == Comment) { // kludge for indicating capture-own on Press
4068       /* Clicked again on same color piece -- changed his mind */
4069       /* note that re-clicking same square always hits same color piece */
4070       second = (x == fromX && y == fromY);
4071       if (appData.highlightDragging) {
4072         SetHighlights(x, y, -1, -1);
4073       } else {
4074         ClearHighlights();
4075       }
4076       if (OKToStartUserMove(x, y)) {
4077         fromX = x;
4078         fromY = y;
4079         DragPieceBegin(event->xbutton.x, event->xbutton.y);
4080       }
4081       return;
4082     }
4083
4084     if(moveType == AmbiguousMove) { // kludge to indicate edit-position move
4085       fromX = fromY = -1; 
4086       ClearHighlights();
4087       DragPieceEnd(event->xbutton.x, event->xbutton.y);
4088       DrawPosition(FALSE, boards[currentMove]);
4089       return;
4090     }
4091
4092     /* Complete move; (x,y) is now different from (fromX, fromY) on both Press and Release */
4093     toX = x;
4094     toY = y;
4095     saveAnimate = appData.animate;
4096     if (event->type == ButtonPress) {
4097         /* Finish clickclick move */
4098         if (appData.animate || appData.highlightLastMove) {
4099             SetHighlights(fromX, fromY, toX, toY);
4100         } else {
4101             ClearHighlights();
4102         }
4103     } else {
4104         /* Finish drag move */
4105         if (appData.highlightLastMove) {
4106             SetHighlights(fromX, fromY, toX, toY);
4107         } else {
4108             ClearHighlights();
4109         }
4110         DragPieceEnd(event->xbutton.x, event->xbutton.y);
4111         /* Don't animate move and drag both */
4112         appData.animate = FALSE;
4113     }
4114     if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||
4115         (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&
4116             appData.alwaysPromoteToQueen) { // promotion, but no choice
4117       FinishMove(moveType, fromX, fromY, toX, toY, 'q');
4118     } else
4119     if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {
4120         SetHighlights(fromX, fromY, toX, toY);
4121         if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {
4122             // [HGM] super: promotion to captured piece selected from holdings
4123             ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];
4124             promotionChoice = TRUE;
4125             // kludge follows to temporarily execute move on display, without promoting yet
4126             boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank
4127             boards[currentMove][toY][toX] = p;
4128             DrawPosition(FALSE, boards[currentMove]);
4129             boards[currentMove][fromY][fromX] = p; // take back, but display stays
4130             boards[currentMove][toY][toX] = q;
4131             DisplayMessage("Click in holdings to choose piece", "");
4132             return;
4133         }
4134         PromotionPopUp();
4135         goto skipClearingFrom; // the skipped stuff is done asynchronously by PromotionCallback
4136     } else
4137     if(moveType != ImpossibleMove) { // valid move, but no promotion
4138       FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);
4139     } else { // invalid move; could have set premove
4140       ClearHighlights();
4141     }
4142             if (!appData.highlightLastMove || gotPremove) ClearHighlights();
4143             if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
4144             fromX = fromY = -1;
4145 skipClearingFrom:
4146     appData.animate = saveAnimate;
4147     if (appData.animate || appData.animateDragging) {
4148         /* Undo animation damage if needed */
4149         DrawPosition(FALSE, NULL);
4150     }
4151 }
4152
4153 void AnimateUserMove (Widget w, XEvent * event,
4154                       String * params, Cardinal * nParams)
4155 {
4156     DragPieceMove(event->xmotion.x, event->xmotion.y);
4157 }
4158
4159 Widget CommentCreate(name, text, mutable, callback, lines)
4160      char *name, *text;
4161      int /*Boolean*/ mutable;
4162      XtCallbackProc callback;
4163      int lines;
4164 {
4165     Arg args[16];
4166     Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
4167     Dimension bw_width;
4168     int j;
4169
4170     j = 0;
4171     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
4172     XtGetValues(boardWidget, args, j);
4173
4174     j = 0;
4175     XtSetArg(args[j], XtNresizable, True);  j++;
4176 #if TOPLEVEL
4177     shell =
4178       XtCreatePopupShell(name, topLevelShellWidgetClass,
4179                          shellWidget, args, j);
4180 #else
4181     shell =
4182       XtCreatePopupShell(name, transientShellWidgetClass,
4183                          shellWidget, args, j);
4184 #endif
4185     layout =
4186       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4187                             layoutArgs, XtNumber(layoutArgs));
4188     form =
4189       XtCreateManagedWidget("form", formWidgetClass, layout,
4190                             formArgs, XtNumber(formArgs));
4191
4192     j = 0;
4193     if (mutable) {
4194         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
4195         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
4196     }
4197     XtSetArg(args[j], XtNstring, text);  j++;
4198     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
4199     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
4200     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
4201     XtSetArg(args[j], XtNright, XtChainRight);  j++;
4202     XtSetArg(args[j], XtNresizable, True);  j++;
4203     XtSetArg(args[j], XtNwidth, bw_width);  j++; /*force wider than buttons*/
4204     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4205     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
4206     XtSetArg(args[j], XtNautoFill, True);  j++;
4207     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4208     edit =
4209       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4210
4211     if (mutable) {
4212         j = 0;
4213         XtSetArg(args[j], XtNfromVert, edit);  j++;
4214         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4215         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4216         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4217         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4218         b_ok =
4219           XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
4220         XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
4221
4222         j = 0;
4223         XtSetArg(args[j], XtNfromVert, edit);  j++;
4224         XtSetArg(args[j], XtNfromHoriz, b_ok);  j++;
4225         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4226         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4227         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4228         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4229         b_cancel =
4230           XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
4231         XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
4232
4233         j = 0;
4234         XtSetArg(args[j], XtNfromVert, edit);  j++;
4235         XtSetArg(args[j], XtNfromHoriz, b_cancel);  j++;
4236         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4237         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4238         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4239         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4240         b_clear =
4241           XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
4242         XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
4243     } else {
4244         j = 0;
4245         XtSetArg(args[j], XtNfromVert, edit);  j++;
4246         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4247         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4248         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4249         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4250         b_close =
4251           XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
4252         XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
4253
4254         j = 0;
4255         XtSetArg(args[j], XtNfromVert, edit);  j++;
4256         XtSetArg(args[j], XtNfromHoriz, b_close);  j++;
4257         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4258         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4259         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4260         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4261         b_edit =
4262           XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
4263         XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
4264     }
4265
4266     XtRealizeWidget(shell);
4267
4268     if (commentX == -1) {
4269         int xx, yy;
4270         Window junk;
4271         Dimension pw_height;
4272         Dimension ew_height;
4273
4274         j = 0;
4275         XtSetArg(args[j], XtNheight, &ew_height);  j++;
4276         XtGetValues(edit, args, j);
4277
4278         j = 0;
4279         XtSetArg(args[j], XtNheight, &pw_height);  j++;
4280         XtGetValues(shell, args, j);
4281         commentH = pw_height + (lines - 1) * ew_height;
4282         commentW = bw_width - 16;
4283
4284         XSync(xDisplay, False);
4285 #ifdef NOTDEF
4286         /* This code seems to tickle an X bug if it is executed too soon
4287            after xboard starts up.  The coordinates get transformed as if
4288            the main window was positioned at (0, 0).
4289            */
4290         XtTranslateCoords(shellWidget,
4291                           (bw_width - commentW) / 2, 0 - commentH / 2,
4292                           &commentX, &commentY);
4293 #else  /*!NOTDEF*/
4294         XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4295                               RootWindowOfScreen(XtScreen(shellWidget)),
4296                               (bw_width - commentW) / 2, 0 - commentH / 2,
4297                               &xx, &yy, &junk);
4298         commentX = xx;
4299         commentY = yy;
4300 #endif /*!NOTDEF*/
4301         if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
4302     }
4303     j = 0;
4304     XtSetArg(args[j], XtNheight, commentH);  j++;
4305     XtSetArg(args[j], XtNwidth, commentW);  j++;
4306     XtSetArg(args[j], XtNx, commentX);  j++;
4307     XtSetArg(args[j], XtNy, commentY);  j++;
4308     XtSetValues(shell, args, j);
4309     XtSetKeyboardFocus(shell, edit);
4310
4311     return shell;
4312 }
4313
4314 /* Used for analysis window and ICS input window */
4315 Widget MiscCreate(name, text, mutable, callback, lines)
4316      char *name, *text;
4317      int /*Boolean*/ mutable;
4318      XtCallbackProc callback;
4319      int lines;
4320 {
4321     Arg args[16];
4322     Widget shell, layout, form, edit;
4323     Position x, y;
4324     Dimension bw_width, pw_height, ew_height, w, h;
4325     int j;
4326     int xx, yy;
4327     Window junk;
4328
4329     j = 0;
4330     XtSetArg(args[j], XtNresizable, True);  j++;
4331 #if TOPLEVEL
4332     shell =
4333       XtCreatePopupShell(name, topLevelShellWidgetClass,
4334                          shellWidget, args, j);
4335 #else
4336     shell =
4337       XtCreatePopupShell(name, transientShellWidgetClass,
4338                          shellWidget, args, j);
4339 #endif
4340     layout =
4341       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4342                             layoutArgs, XtNumber(layoutArgs));
4343     form =
4344       XtCreateManagedWidget("form", formWidgetClass, layout,
4345                             formArgs, XtNumber(formArgs));
4346
4347     j = 0;
4348     if (mutable) {
4349         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
4350         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
4351     }
4352     XtSetArg(args[j], XtNstring, text);  j++;
4353     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
4354     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
4355     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
4356     XtSetArg(args[j], XtNright, XtChainRight);  j++;
4357     XtSetArg(args[j], XtNresizable, True);  j++;
4358     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4359     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
4360     XtSetArg(args[j], XtNautoFill, True);  j++;
4361     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4362     edit =
4363       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4364
4365     XtRealizeWidget(shell);
4366
4367     j = 0;
4368     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
4369     XtGetValues(boardWidget, args, j);
4370
4371     j = 0;
4372     XtSetArg(args[j], XtNheight, &ew_height);  j++;
4373     XtGetValues(edit, args, j);
4374
4375     j = 0;
4376     XtSetArg(args[j], XtNheight, &pw_height);  j++;
4377     XtGetValues(shell, args, j);
4378     h = pw_height + (lines - 1) * ew_height;
4379     w = bw_width - 16;
4380
4381     XSync(xDisplay, False);
4382 #ifdef NOTDEF
4383     /* This code seems to tickle an X bug if it is executed too soon
4384        after xboard starts up.  The coordinates get transformed as if
4385        the main window was positioned at (0, 0).
4386     */
4387     XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
4388 #else  /*!NOTDEF*/
4389     XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4390                           RootWindowOfScreen(XtScreen(shellWidget)),
4391                           (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
4392 #endif /*!NOTDEF*/
4393     x = xx;
4394     y = yy;
4395     if (y < 0) y = 0; /*avoid positioning top offscreen*/
4396
4397     j = 0;
4398     XtSetArg(args[j], XtNheight, h);  j++;
4399     XtSetArg(args[j], XtNwidth, w);  j++;
4400     XtSetArg(args[j], XtNx, x);  j++;
4401     XtSetArg(args[j], XtNy, y);  j++;
4402     XtSetValues(shell, args, j);
4403
4404     return shell;
4405 }
4406
4407
4408 static int savedIndex;  /* gross that this is global */
4409
4410 void EditCommentPopUp(index, title, text)
4411      int index;
4412      char *title, *text;
4413 {
4414     Widget edit;
4415     Arg args[16];
4416     int j;
4417
4418     savedIndex = index;
4419     if (text == NULL) text = "";
4420
4421     if (editShell == NULL) {
4422         editShell =
4423           CommentCreate(title, text, True, EditCommentCallback, 4);
4424         XtRealizeWidget(editShell);
4425         CatchDeleteWindow(editShell, "EditCommentPopDown");
4426     } else {
4427         edit = XtNameToWidget(editShell, "*form.text");
4428         j = 0;
4429         XtSetArg(args[j], XtNstring, text); j++;
4430         XtSetValues(edit, args, j);
4431         j = 0;
4432         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4433         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4434         XtSetValues(editShell, args, j);
4435     }
4436
4437     XtPopup(editShell, XtGrabNone);
4438
4439     editUp = True;
4440     j = 0;
4441     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
4442     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
4443                 args, j);
4444 }
4445
4446 void EditCommentCallback(w, client_data, call_data)
4447      Widget w;
4448      XtPointer client_data, call_data;
4449 {
4450     String name, val;
4451     Arg args[16];
4452     int j;
4453     Widget edit;
4454
4455     j = 0;
4456     XtSetArg(args[j], XtNlabel, &name);  j++;
4457     XtGetValues(w, args, j);
4458
4459     if (strcmp(name, _("ok")) == 0) {
4460         edit = XtNameToWidget(editShell, "*form.text");
4461         j = 0;
4462         XtSetArg(args[j], XtNstring, &val); j++;
4463         XtGetValues(edit, args, j);
4464         ReplaceComment(savedIndex, val);
4465         EditCommentPopDown();
4466     } else if (strcmp(name, _("cancel")) == 0) {
4467         EditCommentPopDown();
4468     } else if (strcmp(name, _("clear")) == 0) {
4469         edit = XtNameToWidget(editShell, "*form.text");
4470         XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4471         XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4472     }
4473 }
4474
4475 void EditCommentPopDown()
4476 {
4477     Arg args[16];
4478     int j;
4479
4480     if (!editUp) return;
4481     j = 0;
4482     XtSetArg(args[j], XtNx, &commentX); j++;
4483     XtSetArg(args[j], XtNy, &commentY); j++;
4484     XtSetArg(args[j], XtNheight, &commentH); j++;
4485     XtSetArg(args[j], XtNwidth, &commentW); j++;
4486     XtGetValues(editShell, args, j);
4487     XtPopdown(editShell);
4488     editUp = False;
4489     j = 0;
4490     XtSetArg(args[j], XtNleftBitmap, None); j++;
4491     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
4492                 args, j);
4493 }
4494
4495 void ICSInputBoxPopUp()
4496 {
4497     Widget edit;
4498     Arg args[16];
4499     int j;
4500     char *title = _("ICS Input");
4501     XtTranslations tr;
4502
4503     if (ICSInputShell == NULL) {
4504         ICSInputShell = MiscCreate(title, "", True, NULL, 1);
4505         tr = XtParseTranslationTable(ICSInputTranslations);
4506         edit = XtNameToWidget(ICSInputShell, "*form.text");
4507         XtOverrideTranslations(edit, tr);
4508         XtRealizeWidget(ICSInputShell);
4509         CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
4510
4511     } else {
4512         edit = XtNameToWidget(ICSInputShell, "*form.text");
4513         j = 0;
4514         XtSetArg(args[j], XtNstring, ""); j++;
4515         XtSetValues(edit, args, j);
4516         j = 0;
4517         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4518         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4519         XtSetValues(ICSInputShell, args, j);
4520     }
4521
4522     XtPopup(ICSInputShell, XtGrabNone);
4523     XtSetKeyboardFocus(ICSInputShell, edit);
4524
4525     ICSInputBoxUp = True;
4526     j = 0;
4527     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
4528     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
4529                 args, j);
4530 }
4531
4532 void ICSInputSendText()
4533 {
4534     Widget edit;
4535     int j;
4536     Arg args[16];
4537     String val;
4538
4539     edit = XtNameToWidget(ICSInputShell, "*form.text");
4540     j = 0;
4541     XtSetArg(args[j], XtNstring, &val); j++;
4542     XtGetValues(edit, args, j);
4543     SendMultiLineToICS(val);
4544     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4545     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4546 }
4547
4548 void ICSInputBoxPopDown()
4549 {
4550     Arg args[16];
4551     int j;
4552
4553     if (!ICSInputBoxUp) return;
4554     j = 0;
4555     XtPopdown(ICSInputShell);
4556     ICSInputBoxUp = False;
4557     j = 0;
4558     XtSetArg(args[j], XtNleftBitmap, None); j++;
4559     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
4560                 args, j);
4561 }
4562
4563 void CommentPopUp(title, text)
4564      char *title, *text;
4565 {
4566     Arg args[16];
4567     int j;
4568     Widget edit;
4569
4570     if (commentShell == NULL) {
4571         commentShell =
4572           CommentCreate(title, text, False, CommentCallback, 4);
4573         XtRealizeWidget(commentShell);
4574         CatchDeleteWindow(commentShell, "CommentPopDown");
4575     } else {
4576         edit = XtNameToWidget(commentShell, "*form.text");
4577         j = 0;
4578         XtSetArg(args[j], XtNstring, text); j++;
4579         XtSetValues(edit, args, j);
4580         j = 0;
4581         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4582         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4583         XtSetValues(commentShell, args, j);
4584     }
4585
4586     XtPopup(commentShell, XtGrabNone);
4587     XSync(xDisplay, False);
4588
4589     commentUp = True;
4590 }
4591
4592 void CommentCallback(w, client_data, call_data)
4593      Widget w;
4594      XtPointer client_data, call_data;
4595 {
4596     String name;
4597     Arg args[16];
4598     int j;
4599
4600     j = 0;
4601     XtSetArg(args[j], XtNlabel, &name);  j++;
4602     XtGetValues(w, args, j);
4603
4604     if (strcmp(name, _("close")) == 0) {
4605         CommentPopDown();
4606     } else if (strcmp(name, _("edit")) == 0) {
4607         CommentPopDown();
4608         EditCommentEvent();
4609     }
4610 }
4611
4612
4613 void CommentPopDown()
4614 {
4615     Arg args[16];
4616     int j;
4617
4618     if (!commentUp) return;
4619     j = 0;
4620     XtSetArg(args[j], XtNx, &commentX); j++;
4621     XtSetArg(args[j], XtNy, &commentY); j++;
4622     XtSetArg(args[j], XtNwidth, &commentW); j++;
4623     XtSetArg(args[j], XtNheight, &commentH); j++;
4624     XtGetValues(commentShell, args, j);
4625     XtPopdown(commentShell);
4626     XSync(xDisplay, False);
4627     commentUp = False;
4628 }
4629
4630 void PromotionPopUp()
4631 {
4632     Arg args[16];
4633     Widget dialog, layout;
4634     Position x, y;
4635     Dimension bw_width, pw_width;
4636     int j;
4637
4638     j = 0;
4639     XtSetArg(args[j], XtNwidth, &bw_width); j++;
4640     XtGetValues(boardWidget, args, j);
4641
4642     j = 0;
4643     XtSetArg(args[j], XtNresizable, True); j++;
4644     XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
4645     promotionShell =
4646       XtCreatePopupShell("Promotion", transientShellWidgetClass,
4647                          shellWidget, args, j);
4648     layout =
4649       XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
4650                             layoutArgs, XtNumber(layoutArgs));
4651
4652     j = 0;
4653     XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
4654     XtSetArg(args[j], XtNborderWidth, 0); j++;
4655     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
4656                                    layout, args, j);
4657
4658   if(gameInfo.variant != VariantShogi) {
4659     XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
4660                        (XtPointer) dialog);
4661     XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
4662                        (XtPointer) dialog);
4663     XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
4664                        (XtPointer) dialog);
4665     XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
4666                        (XtPointer) dialog);
4667     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
4668         gameInfo.variant == VariantGiveaway) {
4669       XawDialogAddButton(dialog, _("King"), PromotionCallback,
4670                          (XtPointer) dialog);
4671     }
4672     if(gameInfo.variant == VariantCapablanca ||
4673        gameInfo.variant == VariantGothic ||
4674        gameInfo.variant == VariantCapaRandom) {
4675       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
4676                          (XtPointer) dialog);
4677       XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
4678                          (XtPointer) dialog);
4679     }
4680   } else // [HGM] shogi
4681   {
4682       XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
4683                          (XtPointer) dialog);
4684       XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
4685                          (XtPointer) dialog);
4686   }
4687     XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
4688                        (XtPointer) dialog);
4689
4690     XtRealizeWidget(promotionShell);
4691     CatchDeleteWindow(promotionShell, "PromotionPopDown");
4692
4693     j = 0;
4694     XtSetArg(args[j], XtNwidth, &pw_width); j++;
4695     XtGetValues(promotionShell, args, j);
4696
4697     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4698                       lineGap + squareSize/3 +
4699                       ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
4700                        0 : 6*(squareSize + lineGap)), &x, &y);
4701
4702     j = 0;
4703     XtSetArg(args[j], XtNx, x); j++;
4704     XtSetArg(args[j], XtNy, y); j++;
4705     XtSetValues(promotionShell, args, j);
4706
4707     XtPopup(promotionShell, XtGrabNone);
4708
4709     promotionUp = True;
4710 }
4711
4712 void PromotionPopDown()
4713 {
4714     if (!promotionUp) return;
4715     XtPopdown(promotionShell);
4716     XtDestroyWidget(promotionShell);
4717     promotionUp = False;
4718 }
4719
4720 void PromotionCallback(w, client_data, call_data)
4721      Widget w;
4722      XtPointer client_data, call_data;
4723 {
4724     String name;
4725     Arg args[16];
4726     int promoChar;
4727
4728     XtSetArg(args[0], XtNlabel, &name);
4729     XtGetValues(w, args, 1);
4730
4731     PromotionPopDown();
4732
4733     if (fromX == -1) return;
4734
4735     if (strcmp(name, _("cancel")) == 0) {
4736         fromX = fromY = -1;
4737         ClearHighlights();
4738         return;
4739     } else if (strcmp(name, _("Knight")) == 0) {
4740         promoChar = 'n';
4741     } else if (strcmp(name, _("Promote")) == 0) {
4742         promoChar = '+';
4743     } else if (strcmp(name, _("Defer")) == 0) {
4744         promoChar = '=';
4745     } else {
4746         promoChar = ToLower(name[0]);
4747     }
4748
4749     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);
4750
4751     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
4752     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
4753     fromX = fromY = -1;
4754 }
4755
4756
4757 void ErrorCallback(w, client_data, call_data)
4758      Widget w;
4759      XtPointer client_data, call_data;
4760 {
4761     errorUp = False;
4762     XtPopdown(w = XtParent(XtParent(XtParent(w))));
4763     XtDestroyWidget(w);
4764     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4765 }
4766
4767
4768 void ErrorPopDown()
4769 {
4770     if (!errorUp) return;
4771     errorUp = False;
4772
4773     if(GUI_Error)
4774       gtk_widget_destroy(GTK_WIDGET(GUI_Error));
4775
4776     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4777
4778     return;
4779 }
4780
4781 void ErrorPopUp(title, label, modal)
4782      char *title, *label;
4783      int modal;
4784 {
4785   GUI_Error = gtk_message_dialog_new(GTK_WINDOW(GUI_Window),
4786                                   GTK_DIALOG_DESTROY_WITH_PARENT,
4787                                   GTK_MESSAGE_ERROR,
4788                                   GTK_BUTTONS_CLOSE,
4789                                   (gchar *)label);
4790
4791   gtk_window_set_title(GTK_WINDOW(GUI_Error),(gchar *) title);
4792   if(modal)
4793     {
4794       gtk_dialog_run(GTK_DIALOG(GUI_Error));
4795       gtk_widget_destroy(GTK_WIDGET(GUI_Error));
4796     }
4797   else
4798     {
4799       g_signal_connect_swapped (GUI_Error, "response",
4800                                 G_CALLBACK (ErrorPopDownProc),
4801                                 GUI_Error);
4802       errorUp = True;
4803       gtk_widget_show(GTK_WIDGET(GUI_Error));
4804     }
4805
4806   return;
4807 }
4808
4809 /* Disable all user input other than deleting the window */
4810 static int frozen = 0;
4811 void FreezeUI()
4812 {
4813   if (frozen) return;
4814   /* Grab by a widget that doesn't accept input */
4815   //  XtAddGrab(messageWidget, TRUE, FALSE);
4816   frozen = 1;
4817 }
4818
4819 /* Undo a FreezeUI */
4820 void ThawUI()
4821 {
4822   if (!frozen) return;
4823   //  XtRemoveGrab(messageWidget);
4824   frozen = 0;
4825 }
4826
4827 char *ModeToWidgetName(mode)
4828      GameMode mode;
4829 {
4830     switch (mode) {
4831       case BeginningOfGame:
4832         if (appData.icsActive)
4833           return "menuMode.ICS Client";
4834         else if (appData.noChessProgram ||
4835                  *appData.cmailGameName != NULLCHAR)
4836           return "menuMode.Edit Game";
4837         else
4838           return "menuMode.Machine Black";
4839       case MachinePlaysBlack:
4840         return "menuMode.Machine Black";
4841       case MachinePlaysWhite:
4842         return "menuMode.Machine White";
4843       case AnalyzeMode:
4844         return "menuMode.Analysis Mode";
4845       case AnalyzeFile:
4846         return "menuMode.Analyze File";
4847       case TwoMachinesPlay:
4848         return "menuMode.Two Machines";
4849       case EditGame:
4850         return "menuMode.Edit Game";
4851       case PlayFromGameFile:
4852         return "menuFile.Load Game";
4853       case EditPosition:
4854         return "menuMode.Edit Position";
4855       case Training:
4856         return "menuMode.Training";
4857       case IcsPlayingWhite:
4858       case IcsPlayingBlack:
4859       case IcsObserving:
4860       case IcsIdle:
4861       case IcsExamining:
4862         return "menuMode.ICS Client";
4863       default:
4864       case EndOfGame:
4865         return NULL;
4866     }
4867 }
4868
4869 void ModeHighlight()
4870 {
4871     static int oldPausing = FALSE;
4872     static GameMode oldmode = (GameMode) -1;
4873     char *wname;
4874
4875    // todo this toggling of the pause button doesn't seem to work?
4876     // e.g. select pause from buttonbar doesn't activate menumode.pause
4877
4878     //    if (!boardWidget || !XtIsRealized(boardWidget)) return;
4879
4880     if (pausing != oldPausing) {
4881       oldPausing = pausing;
4882       gtk_button_set_relief(GTK_BUTTON (gtk_builder_get_object (builder, "menuMode.Pause")),pausing?GTK_RELIEF_NORMAL:GTK_RELIEF_NONE);
4883       /* toggle background color in showbuttonbar */
4884       if (appData.showButtonBar) {
4885         if (pausing) {
4886           gtk_button_pressed(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
4887         } else {
4888           gtk_button_released(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
4889         }
4890       }
4891     }
4892
4893     wname = ModeToWidgetName(oldmode);
4894     if(wname)
4895        gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, wname)),True);
4896
4897     oldmode = gameMode;
4898
4899     /* Maybe all the enables should be handled here, not just this one */
4900     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuMode.Training")),
4901                              gameMode == Training || gameMode == PlayFromGameFile);
4902 }
4903
4904
4905 /*
4906  * Button/menu procedures
4907  */
4908
4909 int LoadGamePopUp(f, gameNumber, title)
4910      FILE *f;
4911      int gameNumber;
4912      char *title;
4913 {
4914     cmailMsgLoaded = FALSE;
4915
4916     if (gameNumber == 0) 
4917       {
4918         int error = GameListBuild(f);
4919
4920         if (error) 
4921           {
4922             DisplayError(_("Cannot build game list"), error);
4923           } 
4924         else if (!ListEmpty(&gameList) 
4925                  && ((ListGame *) gameList.tailPred)->number > 1) 
4926           {
4927             // TODO convert to GTK
4928             //      GameListPopUp(f, title);
4929             return TRUE;
4930           };
4931
4932         GameListDestroy();
4933         gameNumber = 1;
4934       };
4935
4936     return LoadGame(f, gameNumber, title, FALSE);
4937 }
4938
4939 void ReloadCmailMsgProc(w, event, prms, nprms)
4940      Widget w;
4941      XEvent *event;
4942      String *prms;
4943      Cardinal *nprms;
4944 {
4945     ReloadCmailMsgEvent(FALSE);
4946 }
4947
4948 void MailMoveProc(w, event, prms, nprms)
4949      Widget w;
4950      XEvent *event;
4951      String *prms;
4952      Cardinal *nprms;
4953 {
4954     MailMoveEvent();
4955 }
4956
4957 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4958 static char *selected_fen_position=NULL;
4959
4960 static Boolean
4961 SendPositionSelection(Widget w, Atom *selection, Atom *target,
4962                  Atom *type_return, XtPointer *value_return,
4963                  unsigned long *length_return, int *format_return)
4964 {
4965   char *selection_tmp;
4966
4967   if (!selected_fen_position) return False; /* should never happen */
4968   if (*target == XA_STRING){
4969     /* note: since no XtSelectionDoneProc was registered, Xt will
4970      * automatically call XtFree on the value returned.  So have to
4971      * make a copy of it allocated with XtMalloc */
4972     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4973     strcpy(selection_tmp, selected_fen_position);
4974
4975     *value_return=selection_tmp;
4976     *length_return=strlen(selection_tmp);
4977     *type_return=XA_STRING;
4978     *format_return = 8; /* bits per byte */
4979     return True;
4980   } else {
4981     return False;
4982   }
4983 }
4984
4985 /* note: when called from menu all parameters are NULL, so no clue what the
4986  * Widget which was clicked on was, or what the click event was
4987  */
4988 void CopyPositionProc(w, event, prms, nprms)
4989   Widget w;
4990   XEvent *event;
4991   String *prms;
4992   Cardinal *nprms;
4993   {
4994     int ret;
4995
4996     if (selected_fen_position) free(selected_fen_position);
4997     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
4998     if (!selected_fen_position) return;
4999     ret = XtOwnSelection(menuBarWidget, XA_PRIMARY,
5000                          CurrentTime,
5001                          SendPositionSelection,
5002                          NULL/* lose_ownership_proc */ ,
5003                          NULL/* transfer_done_proc */);
5004     if (!ret) {
5005       free(selected_fen_position);
5006       selected_fen_position=NULL;
5007     }
5008   }
5009
5010 /* function called when the data to Paste is ready */
5011 static void
5012 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5013            Atom *type, XtPointer value, unsigned long *len, int *format)
5014 {
5015   char *fenstr=value;
5016   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5017   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5018   EditPositionPasteFEN(fenstr);
5019   XtFree(value);
5020 }
5021
5022 /* called when Paste Position button is pressed,
5023  * all parameters will be NULL */
5024 void PastePositionProc(w, event, prms, nprms)
5025   Widget w;
5026   XEvent *event;
5027   String *prms;
5028   Cardinal *nprms;
5029 {
5030     XtGetSelectionValue(menuBarWidget, XA_PRIMARY, XA_STRING,
5031       /* (XtSelectionCallbackProc) */ PastePositionCB,
5032       NULL, /* client_data passed to PastePositionCB */
5033
5034       /* better to use the time field from the event that triggered the
5035        * call to this function, but that isn't trivial to get
5036        */
5037       CurrentTime
5038     );
5039     return;
5040 }
5041
5042 static Boolean
5043 SendGameSelection(Widget w, Atom *selection, Atom *target,
5044                   Atom *type_return, XtPointer *value_return,
5045                   unsigned long *length_return, int *format_return)
5046 {
5047   char *selection_tmp;
5048
5049   if (*target == XA_STRING){
5050     FILE* f = fopen(gameCopyFilename, "r");
5051     long len;
5052     size_t count;
5053     if (f == NULL) return False;
5054     fseek(f, 0, 2);
5055     len = ftell(f);
5056     rewind(f);
5057     selection_tmp = XtMalloc(len + 1);
5058     count = fread(selection_tmp, 1, len, f);
5059     if (len != count) {
5060       XtFree(selection_tmp);
5061       return False;
5062     }
5063     selection_tmp[len] = NULLCHAR;
5064     *value_return = selection_tmp;
5065     *length_return = len;
5066     *type_return = XA_STRING;
5067     *format_return = 8; /* bits per byte */
5068     return True;
5069   } else {
5070     return False;
5071   }
5072 }
5073
5074 /* note: when called from menu all parameters are NULL, so no clue what the
5075  * Widget which was clicked on was, or what the click event was
5076  */
5077 void CopyGameProc(w, event, prms, nprms)
5078   Widget w;
5079   XEvent *event;
5080   String *prms;
5081   Cardinal *nprms;
5082 {
5083   int ret;
5084
5085   ret = SaveGameToFile(gameCopyFilename, FALSE);
5086   if (!ret) return;
5087
5088   ret = XtOwnSelection(menuBarWidget, XA_PRIMARY,
5089                        CurrentTime,
5090                        SendGameSelection,
5091                        NULL/* lose_ownership_proc */ ,
5092                        NULL/* transfer_done_proc */);
5093 }
5094
5095 /* function called when the data to Paste is ready */
5096 static void
5097 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5098             Atom *type, XtPointer value, unsigned long *len, int *format)
5099 {
5100   FILE* f;
5101   if (value == NULL || *len == 0) {
5102     return; /* nothing had been selected to copy */
5103   }
5104   f = fopen(gamePasteFilename, "w");
5105   if (f == NULL) {
5106     DisplayError(_("Can't open temp file"), errno);
5107     return;
5108   }
5109   fwrite(value, 1, *len, f);
5110   fclose(f);
5111   XtFree(value);
5112   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5113 }
5114
5115 /* called when Paste Game button is pressed,
5116  * all parameters will be NULL */
5117 void PasteGameProc(w, event, prms, nprms)
5118   Widget w;
5119   XEvent *event;
5120   String *prms;
5121   Cardinal *nprms;
5122 {
5123     XtGetSelectionValue(menuBarWidget, XA_PRIMARY, XA_STRING,
5124       /* (XtSelectionCallbackProc) */ PasteGameCB,
5125       NULL, /* client_data passed to PasteGameCB */
5126
5127       /* better to use the time field from the event that triggered the
5128        * call to this function, but that isn't trivial to get
5129        */
5130       CurrentTime
5131     );
5132     return;
5133 }
5134
5135
5136 void AutoSaveGame()
5137 {
5138   SaveGameProc(NULL, NULL);
5139   return;
5140 }
5141
5142 void AnalyzeModeProc(w, event, prms, nprms)
5143      Widget w;
5144      XEvent *event;
5145      String *prms;
5146      Cardinal *nprms;
5147 {
5148     char buf[MSG_SIZ];
5149
5150     if (!first.analysisSupport) {
5151       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5152       DisplayError(buf, 0);
5153       return;
5154     }
5155     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5156     if (appData.icsActive) {
5157         if (gameMode != IcsObserving) {
5158             sprintf(buf,_("You are not observing a game"));
5159             DisplayError(buf, 0);
5160             /* secure check */
5161             if (appData.icsEngineAnalyze) {
5162                 if (appData.debugMode)
5163                     fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5164                 ExitAnalyzeMode();
5165                 ModeHighlight();
5166             }
5167             return;
5168         }
5169         /* if enable, use want disable icsEngineAnalyze */
5170         if (appData.icsEngineAnalyze) {
5171                 ExitAnalyzeMode();
5172                 ModeHighlight();
5173                 return;
5174         }
5175         appData.icsEngineAnalyze = TRUE;
5176         if (appData.debugMode)
5177             fprintf(debugFP, _("ICS engine analyze starting... \n"));
5178     }
5179     if (!appData.showThinking)
5180       ShowThinkingProc(NULL,NULL);
5181
5182     AnalyzeModeEvent();
5183 }
5184
5185 void EditGameProc(w, event, prms, nprms)
5186      Widget w;
5187      XEvent *event;
5188      String *prms;
5189      Cardinal *nprms;
5190 {
5191     EditGameEvent();
5192 }
5193
5194 void EditPositionProc(w, event, prms, nprms)
5195      Widget w;
5196      XEvent *event;
5197      String *prms;
5198      Cardinal *nprms;
5199 {
5200     EditPositionEvent();
5201 }
5202
5203 void TrainingProc(w, event, prms, nprms)
5204      Widget w;
5205      XEvent *event;
5206      String *prms;
5207      Cardinal *nprms;
5208 {
5209     TrainingEvent();
5210 }
5211
5212 void EditCommentProc(w, event, prms, nprms)
5213      Widget w;
5214      XEvent *event;
5215      String *prms;
5216      Cardinal *nprms;
5217 {
5218     if (editUp) {
5219         EditCommentPopDown();
5220     } else {
5221         EditCommentEvent();
5222     }
5223 }
5224
5225 void IcsInputBoxProc(w, event, prms, nprms)
5226      Widget w;
5227      XEvent *event;
5228      String *prms;
5229      Cardinal *nprms;
5230 {
5231     if (ICSInputBoxUp) {
5232         ICSInputBoxPopDown();
5233     } else {
5234         ICSInputBoxPopUp();
5235     }
5236 }
5237
5238
5239 void EnterKeyProc(w, event, prms, nprms)
5240      Widget w;
5241      XEvent *event;
5242      String *prms;
5243      Cardinal *nprms;
5244 {
5245     if (ICSInputBoxUp == True)
5246       ICSInputSendText();
5247 }
5248
5249
5250 void PonderNextMoveProc(w, event, prms, nprms)
5251      Widget w;
5252      XEvent *event;
5253      String *prms;
5254      Cardinal *nprms;
5255 {
5256     Arg args[16];
5257
5258     PonderNextMoveEvent(!appData.ponderNextMove);
5259
5260     if (appData.ponderNextMove) {
5261         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5262     } else {
5263         XtSetArg(args[0], XtNleftBitmap, None);
5264     }
5265     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
5266                 args, 1);
5267 }
5268
5269 void PopupExitMessageProc(w, event, prms, nprms)
5270      Widget w;
5271      XEvent *event;
5272      String *prms;
5273      Cardinal *nprms;
5274 {
5275     Arg args[16];
5276
5277     appData.popupExitMessage = !appData.popupExitMessage;
5278
5279     if (appData.popupExitMessage) {
5280         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5281     } else {
5282         XtSetArg(args[0], XtNleftBitmap, None);
5283     }
5284     XtSetValues(XtNameToWidget(menuBarWidget,
5285                                "menuOptions.Popup Exit Message"), args, 1);
5286 }
5287
5288 void PopupMoveErrorsProc(w, event, prms, nprms)
5289      Widget w;
5290      XEvent *event;
5291      String *prms;
5292      Cardinal *nprms;
5293 {
5294     Arg args[16];
5295
5296     appData.popupMoveErrors = !appData.popupMoveErrors;
5297
5298     if (appData.popupMoveErrors) {
5299         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5300     } else {
5301         XtSetArg(args[0], XtNleftBitmap, None);
5302     }
5303     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
5304                 args, 1);
5305 }
5306
5307 void PremoveProc(w, event, prms, nprms)
5308      Widget w;
5309      XEvent *event;
5310      String *prms;
5311      Cardinal *nprms;
5312 {
5313     Arg args[16];
5314
5315     appData.premove = !appData.premove;
5316
5317     if (appData.premove) {
5318         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5319     } else {
5320         XtSetArg(args[0], XtNleftBitmap, None);
5321     }
5322     XtSetValues(XtNameToWidget(menuBarWidget,
5323                                "menuOptions.Premove"), args, 1);
5324 }
5325
5326 void QuietPlayProc(w, event, prms, nprms)
5327      Widget w;
5328      XEvent *event;
5329      String *prms;
5330      Cardinal *nprms;
5331 {
5332     Arg args[16];
5333
5334     appData.quietPlay = !appData.quietPlay;
5335
5336     if (appData.quietPlay) {
5337         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5338     } else {
5339         XtSetArg(args[0], XtNleftBitmap, None);
5340     }
5341     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Quiet Play"),
5342                 args, 1);
5343 }
5344
5345 void DebugProc(w, event, prms, nprms)
5346      Widget w;
5347      XEvent *event;
5348      String *prms;
5349      Cardinal *nprms;
5350 {
5351     appData.debugMode = !appData.debugMode;
5352 }
5353
5354 void AboutGameProc(w, event, prms, nprms)
5355      Widget w;
5356      XEvent *event;
5357      String *prms;
5358      Cardinal *nprms;
5359 {
5360     AboutGameEvent();
5361 }
5362
5363 void NothingProc(w, event, prms, nprms)
5364      Widget w;
5365      XEvent *event;
5366      String *prms;
5367      Cardinal *nprms;
5368 {
5369     return;
5370 }
5371
5372 void Iconify(w, event, prms, nprms)
5373      Widget w;
5374      XEvent *event;
5375      String *prms;
5376      Cardinal *nprms;
5377 {
5378     Arg args[16];
5379
5380     fromX = fromY = -1;
5381     XtSetArg(args[0], XtNiconic, True);
5382     XtSetValues(shellWidget, args, 1);
5383 }
5384
5385 void DisplayMessage(message, extMessage)
5386      gchar *message, *extMessage;
5387 {
5388     char buf[MSG_SIZ];
5389     Arg arg;
5390
5391     if (extMessage) {
5392         if (*message) {
5393             snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
5394             message = buf;
5395         } else {
5396             message = extMessage;
5397         }
5398     }
5399     gtk_label_set_text( GTK_LABEL(gtk_builder_get_object (builder, "Messages")),message);
5400
5401     return;
5402 }
5403
5404 void DisplayTitle(text)
5405      char *text;
5406 {
5407     gchar title[MSG_SIZ];
5408
5409     if (text == NULL) text = "";
5410
5411     if (appData.titleInWindow)
5412       {
5413         /* TODO */
5414       }
5415
5416     if (*text != NULLCHAR)
5417       {
5418         strcpy(title, text);
5419       }
5420     else if (appData.icsActive)
5421       {
5422         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
5423       }
5424     else if (appData.cmailGameName[0] != NULLCHAR)
5425       {
5426         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
5427 #ifdef GOTHIC
5428     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
5429       }
5430     else if (gameInfo.variant == VariantGothic)
5431       {
5432         strcpy(title, GOTHIC);
5433 #endif
5434 #ifdef FALCON
5435       }
5436     else if (gameInfo.variant == VariantFalcon)
5437       {
5438         strcpy(title, FALCON);
5439 #endif
5440       }
5441     else if (appData.noChessProgram)
5442       {
5443         strcpy(title, programName);
5444       }
5445     else
5446       {
5447         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
5448       }
5449     gtk_window_set_title(GTK_WINDOW(GUI_Window),title);
5450
5451     return;
5452 }
5453
5454
5455 void DisplayError(message, error)
5456      String message;
5457      int error;
5458 {
5459     char buf[MSG_SIZ];
5460
5461     if (error == 0) {
5462         if (appData.debugMode || appData.matchMode) {
5463             fprintf(stderr, "%s: %s\n", programName, message);
5464         }
5465     } else {
5466         if (appData.debugMode || appData.matchMode) {
5467             fprintf(stderr, "%s: %s: %s\n",
5468                     programName, message, strerror(error));
5469         }
5470         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
5471         message = buf;
5472     }
5473     ErrorPopUp(_("Error"), message, FALSE);
5474 }
5475
5476
5477 void DisplayMoveError(message)
5478      String message;
5479 {
5480     fromX = fromY = -1;
5481     ClearHighlights();
5482     DrawPosition(FALSE, NULL);
5483     if (appData.debugMode || appData.matchMode) {
5484         fprintf(stderr, "%s: %s\n", programName, message);
5485     }
5486     if (appData.popupMoveErrors) {
5487         ErrorPopUp(_("Error"), message, FALSE);
5488     } else {
5489         DisplayMessage(message, "");
5490     }
5491 }
5492
5493
5494 void DisplayFatalError(message, error, status)
5495      String message;
5496      int error, status;
5497 {
5498     char buf[MSG_SIZ];
5499
5500     errorExitStatus = status;
5501     if (error == 0) {
5502         fprintf(stderr, "%s: %s\n", programName, message);
5503     } else {
5504         fprintf(stderr, "%s: %s: %s\n",
5505                 programName, message, strerror(error));
5506         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
5507         message = buf;
5508     }
5509     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
5510       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
5511     } else {
5512       ExitEvent(status);
5513     }
5514 }
5515
5516 void DisplayInformation(message)
5517      String message;
5518 {
5519     ErrorPopDown();
5520     ErrorPopUp(_("Information"), message, TRUE);
5521 }
5522
5523 void DisplayNote(message)
5524      String message;
5525 {
5526     ErrorPopDown();
5527     ErrorPopUp(_("Note"), message, FALSE);
5528 }
5529
5530 static int
5531 NullXErrorCheck(dpy, error_event)
5532      Display *dpy;
5533      XErrorEvent *error_event;
5534 {
5535     return 0;
5536 }
5537
5538 void DisplayIcsInteractionTitle(message)
5539      String message;
5540 {
5541   if (oldICSInteractionTitle == NULL) {
5542     /* Magic to find the old window title, adapted from vim */
5543     char *wina = getenv("WINDOWID");
5544     if (wina != NULL) {
5545       Window win = (Window) atoi(wina);
5546       Window root, parent, *children;
5547       unsigned int nchildren;
5548       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
5549       for (;;) {
5550         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
5551         if (!XQueryTree(xDisplay, win, &root, &parent,
5552                         &children, &nchildren)) break;
5553         if (children) XFree((void *)children);
5554         if (parent == root || parent == 0) break;
5555         win = parent;
5556       }
5557       XSetErrorHandler(oldHandler);
5558     }
5559     if (oldICSInteractionTitle == NULL) {
5560       oldICSInteractionTitle = "xterm";
5561     }
5562   }
5563   printf("\033]0;%s\007", message);
5564   fflush(stdout);
5565 }
5566
5567 char pendingReplyPrefix[MSG_SIZ];
5568 ProcRef pendingReplyPR;
5569
5570 void AskQuestionProc(w, event, prms, nprms)
5571      Widget w;
5572      XEvent *event;
5573      String *prms;
5574      Cardinal *nprms;
5575 {
5576     if (*nprms != 4) {
5577         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
5578                 *nprms);
5579         return;
5580     }
5581     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
5582 }
5583
5584 void AskQuestionPopDown()
5585 {
5586     if (!askQuestionUp) return;
5587     XtPopdown(askQuestionShell);
5588     XtDestroyWidget(askQuestionShell);
5589     askQuestionUp = False;
5590 }
5591
5592 void AskQuestionReplyAction(w, event, prms, nprms)
5593      Widget w;
5594      XEvent *event;
5595      String *prms;
5596      Cardinal *nprms;
5597 {
5598     char buf[MSG_SIZ];
5599     int err;
5600     String reply;
5601
5602     reply = XawDialogGetValueString(w = XtParent(w));
5603     strcpy(buf, pendingReplyPrefix);
5604     if (*buf) strcat(buf, " ");
5605     strcat(buf, reply);
5606     strcat(buf, "\n");
5607     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
5608     AskQuestionPopDown();
5609
5610     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
5611 }
5612
5613 void AskQuestionCallback(w, client_data, call_data)
5614      Widget w;
5615      XtPointer client_data, call_data;
5616 {
5617     String name;
5618     Arg args[16];
5619
5620     XtSetArg(args[0], XtNlabel, &name);
5621     XtGetValues(w, args, 1);
5622
5623     if (strcmp(name, _("cancel")) == 0) {
5624         AskQuestionPopDown();
5625     } else {
5626         AskQuestionReplyAction(w, NULL, NULL, NULL);
5627     }
5628 }
5629
5630 void AskQuestion(title, question, replyPrefix, pr)
5631      char *title, *question, *replyPrefix;
5632      ProcRef pr;
5633 {
5634     Arg args[16];
5635     Widget popup, layout, dialog, edit;
5636     Window root, child;
5637     int x, y, i;
5638     int win_x, win_y;
5639     unsigned int mask;
5640
5641     strcpy(pendingReplyPrefix, replyPrefix);
5642     pendingReplyPR = pr;
5643
5644     i = 0;
5645     XtSetArg(args[i], XtNresizable, True); i++;
5646     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
5647     askQuestionShell = popup =
5648       XtCreatePopupShell(title, transientShellWidgetClass,
5649                          shellWidget, args, i);
5650
5651     layout =
5652       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
5653                             layoutArgs, XtNumber(layoutArgs));
5654
5655     i = 0;
5656     XtSetArg(args[i], XtNlabel, question); i++;
5657     XtSetArg(args[i], XtNvalue, ""); i++;
5658     XtSetArg(args[i], XtNborderWidth, 0); i++;
5659     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
5660                                    layout, args, i);
5661
5662     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
5663                        (XtPointer) dialog);
5664     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
5665                        (XtPointer) dialog);
5666
5667     XtRealizeWidget(popup);
5668     CatchDeleteWindow(popup, "AskQuestionPopDown");
5669
5670     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
5671                   &x, &y, &win_x, &win_y, &mask);
5672
5673     XtSetArg(args[0], XtNx, x - 10);
5674     XtSetArg(args[1], XtNy, y - 30);
5675     XtSetValues(popup, args, 2);
5676
5677     XtPopup(popup, XtGrabExclusive);
5678     askQuestionUp = True;
5679
5680     edit = XtNameToWidget(dialog, "*value");
5681     XtSetKeyboardFocus(popup, edit);
5682 }
5683
5684
5685 void
5686 PlaySound(name)
5687      char *name;
5688 {
5689   if (*name == NULLCHAR) {
5690     return;
5691   } else if (strcmp(name, "$") == 0) {
5692     putc(BELLCHAR, stderr);
5693   } else {
5694     char buf[2048];
5695     snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
5696     system(buf);
5697   }
5698 }
5699
5700 void
5701 RingBell()
5702 {
5703   PlaySound(appData.soundMove);
5704 }
5705
5706 void
5707 PlayIcsWinSound()
5708 {
5709   PlaySound(appData.soundIcsWin);
5710 }
5711
5712 void
5713 PlayIcsLossSound()
5714 {
5715   PlaySound(appData.soundIcsLoss);
5716 }
5717
5718 void
5719 PlayIcsDrawSound()
5720 {
5721   PlaySound(appData.soundIcsDraw);
5722 }
5723
5724 void
5725 PlayIcsUnfinishedSound()
5726 {
5727   PlaySound(appData.soundIcsUnfinished);
5728 }
5729
5730 void
5731 PlayAlarmSound()
5732 {
5733   PlaySound(appData.soundIcsAlarm);
5734 }
5735
5736 void
5737 EchoOn()
5738 {
5739     system("stty echo");
5740 }
5741
5742 void
5743 EchoOff()
5744 {
5745     system("stty -echo");
5746 }
5747
5748 void
5749 Colorize(cc, continuation)
5750      ColorClass cc;
5751      int continuation;
5752 {
5753     char buf[MSG_SIZ];
5754     int count, outCount, error;
5755
5756     if (textColors[(int)cc].bg > 0) {
5757         if (textColors[(int)cc].fg > 0) {
5758             sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
5759                     textColors[(int)cc].fg, textColors[(int)cc].bg);
5760         } else {
5761             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
5762                     textColors[(int)cc].bg);
5763         }
5764     } else {
5765         if (textColors[(int)cc].fg > 0) {
5766             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
5767                     textColors[(int)cc].fg);
5768         } else {
5769             sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
5770         }
5771     }
5772     count = strlen(buf);
5773     outCount = OutputToProcess(NoProc, buf, count, &error);
5774     if (outCount < count) {
5775         DisplayFatalError(_("Error writing to display"), error, 1);
5776     }
5777
5778     if (continuation) return;
5779     switch (cc) {
5780     case ColorShout:
5781       PlaySound(appData.soundShout);
5782       break;
5783     case ColorSShout:
5784       PlaySound(appData.soundSShout);
5785       break;
5786     case ColorChannel1:
5787       PlaySound(appData.soundChannel1);
5788       break;
5789     case ColorChannel:
5790       PlaySound(appData.soundChannel);
5791       break;
5792     case ColorKibitz:
5793       PlaySound(appData.soundKibitz);
5794       break;
5795     case ColorTell:
5796       PlaySound(appData.soundTell);
5797       break;
5798     case ColorChallenge:
5799       PlaySound(appData.soundChallenge);
5800       break;
5801     case ColorRequest:
5802       PlaySound(appData.soundRequest);
5803       break;
5804     case ColorSeek:
5805       PlaySound(appData.soundSeek);
5806       break;
5807     case ColorNormal:
5808     case ColorNone:
5809     default:
5810       break;
5811     }
5812 }
5813
5814 char *UserName()
5815 {
5816     return getpwuid(getuid())->pw_name;
5817 }
5818
5819 static char *ExpandPathName(path)
5820      char *path;
5821 {
5822     static char static_buf[2000];
5823     char *d, *s, buf[2000];
5824     struct passwd *pwd;
5825
5826     s = path;
5827     d = static_buf;
5828
5829     while (*s && isspace(*s))
5830       ++s;
5831
5832     if (!*s) {
5833         *d = 0;
5834         return static_buf;
5835     }
5836
5837     if (*s == '~') {
5838         if (*(s+1) == '/') {
5839             strcpy(d, getpwuid(getuid())->pw_dir);
5840             strcat(d, s+1);
5841         }
5842         else {
5843             strcpy(buf, s+1);
5844             *strchr(buf, '/') = 0;
5845             pwd = getpwnam(buf);
5846             if (!pwd)
5847               {
5848                   fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
5849                           buf, path);
5850                   return NULL;
5851               }
5852             strcpy(d, pwd->pw_dir);
5853             strcat(d, strchr(s+1, '/'));
5854         }
5855     }
5856     else
5857       strcpy(d, s);
5858
5859     return static_buf;
5860 }
5861
5862 char *HostName()
5863 {
5864     static char host_name[MSG_SIZ];
5865
5866 #if HAVE_GETHOSTNAME
5867     gethostname(host_name, MSG_SIZ);
5868     return host_name;
5869 #else  /* not HAVE_GETHOSTNAME */
5870 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
5871     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
5872     return host_name;
5873 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
5874     return "localhost";
5875 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
5876 #endif /* not HAVE_GETHOSTNAME */
5877 }
5878
5879 guint delayedEventTimerTag = 0;
5880 DelayedEventCallback delayedEventCallback = 0;
5881
5882 void
5883 FireDelayedEvent(data)
5884      gpointer data;
5885 {
5886   /* remove timer */
5887   g_source_remove(delayedEventTimerTag);
5888   delayedEventTimerTag = 0;
5889
5890   /* call function */
5891   delayedEventCallback();
5892
5893   return;
5894 }
5895
5896 void
5897 ScheduleDelayedEvent(cb, millisec)
5898      DelayedEventCallback cb; guint millisec;
5899 {
5900     if(delayedEventTimerTag && delayedEventCallback == cb)
5901         // [HGM] alive: replace, rather than add or flush identical event
5902         g_source_remove(delayedEventTimerTag);
5903     delayedEventCallback = cb;
5904     delayedEventTimerTag = g_timeout_add(millisec,(GSourceFunc) FireDelayedEvent, NULL);
5905     return;
5906 }
5907
5908 DelayedEventCallback
5909 GetDelayedEvent()
5910 {
5911   if (delayedEventTimerTag)
5912     {
5913       return delayedEventCallback;
5914     }
5915   else
5916     {
5917       return NULL;
5918     }
5919 }
5920
5921 void
5922 CancelDelayedEvent()
5923 {
5924   if (delayedEventTimerTag)
5925     {
5926       g_source_remove(delayedEventTimerTag);
5927       delayedEventTimerTag = 0;
5928     }
5929
5930   return;
5931 }
5932
5933 guint loadGameTimerTag = 0;
5934
5935 int LoadGameTimerRunning()
5936 {
5937     return loadGameTimerTag != 0;
5938 }
5939
5940 int StopLoadGameTimer()
5941 {
5942     if (loadGameTimerTag != 0) {
5943         g_source_remove(loadGameTimerTag);
5944         loadGameTimerTag = 0;
5945         return TRUE;
5946     } else {
5947         return FALSE;
5948     }
5949 }
5950
5951 void
5952 LoadGameTimerCallback(data)
5953      gpointer data;
5954 {
5955   /* remove timer */
5956   g_source_remove(loadGameTimerTag);
5957   loadGameTimerTag = 0;
5958
5959   AutoPlayGameLoop();
5960   return;
5961 }
5962
5963 void
5964 StartLoadGameTimer(millisec)
5965      long millisec;
5966 {
5967   loadGameTimerTag =
5968     g_timeout_add( millisec, (GSourceFunc) LoadGameTimerCallback, NULL);
5969   return;
5970 }
5971
5972 guint analysisClockTag = 0;
5973
5974 gboolean
5975 AnalysisClockCallback(data)
5976      gpointer data;
5977 {
5978     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
5979          || appData.icsEngineAnalyze)
5980       {
5981         AnalysisPeriodicEvent(0);
5982         return 1; /* keep on going */
5983       }
5984     return 0; /* stop timer */
5985 }
5986
5987 void
5988 StartAnalysisClock()
5989 {
5990   analysisClockTag =
5991     g_timeout_add( 2000,(GSourceFunc) AnalysisClockCallback, NULL);
5992   return;
5993 }
5994
5995 guint clockTimerTag = 0;
5996
5997 int ClockTimerRunning()
5998 {
5999     return clockTimerTag != 0;
6000 }
6001
6002 int StopClockTimer()
6003 {
6004     if (clockTimerTag != 0)
6005       {
6006         g_source_remove(clockTimerTag);
6007         clockTimerTag = 0;
6008         return TRUE;
6009       }
6010     else
6011       {
6012         return FALSE;
6013       }
6014 }
6015
6016 void
6017 ClockTimerCallback(data)
6018      gpointer data;
6019 {
6020   /* remove timer */
6021   g_source_remove(clockTimerTag);
6022   clockTimerTag = 0;
6023
6024   DecrementClocks();
6025   return;
6026 }
6027
6028 void
6029 StartClockTimer(millisec)
6030      long millisec;
6031 {
6032   clockTimerTag = g_timeout_add(millisec,(GSourceFunc) ClockTimerCallback,NULL);
6033   return;
6034 }
6035
6036 void
6037 DisplayTimerLabel(w, color, timer, highlight)
6038      GtkWidget *w;
6039      char *color;
6040      long timer;
6041      int highlight;
6042 {
6043   gchar buf[MSG_SIZ];
6044
6045
6046   if (appData.clockMode) {
6047     sprintf(buf, "%s: %s", color, TimeString(timer));
6048   } else {
6049     sprintf(buf, "%s  ", color);
6050   }
6051   gtk_label_set_text(GTK_LABEL(w),buf);
6052
6053   /* check for low time warning */
6054 //    Pixel foregroundOrWarningColor = timerForegroundPixel;
6055
6056 //    if (timer > 0 &&
6057 //        appData.lowTimeWarning &&
6058 //        (timer / 1000) < appData.icsAlarmTime)
6059 //      foregroundOrWarningColor = lowTimeWarningColor;
6060 //
6061 //    if (appData.clockMode) {
6062 //      sprintf(buf, "%s: %s", color, TimeString(timer));
6063 //      XtSetArg(args[0], XtNlabel, buf);
6064 //    } else {
6065 //      sprintf(buf, "%s  ", color);
6066 //      XtSetArg(args[0], XtNlabel, buf);
6067 //    }
6068 //
6069 //    if (highlight) {
6070 //
6071 //      XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
6072 //      XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
6073 //    } else {
6074 //      XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
6075 //      XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
6076 //    }
6077 //
6078 //    XtSetValues(w, args, 3);
6079 //
6080 }
6081
6082 void
6083 DisplayWhiteClock(timeRemaining, highlight)
6084      long timeRemaining;
6085      int highlight;
6086 {
6087   if(appData.noGUI) return;
6088
6089   DisplayTimerLabel(GUI_Whiteclock, _("White"), timeRemaining, highlight);
6090   if (highlight && WindowIcon == BlackIcon)
6091     {
6092       WindowIcon = WhiteIcon;
6093       gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
6094     }
6095 }
6096
6097 void
6098 DisplayBlackClock(timeRemaining, highlight)
6099      long timeRemaining;
6100      int highlight;
6101 {
6102     if(appData.noGUI) return;
6103
6104     DisplayTimerLabel(GUI_Blackclock, _("Black"), timeRemaining, highlight);
6105     if (highlight && WindowIcon == WhiteIcon)
6106       {
6107         WindowIcon = BlackIcon;
6108         gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
6109       }
6110 }
6111
6112 #define CPNone 0
6113 #define CPReal 1
6114 #define CPComm 2
6115 #define CPSock 3
6116 #define CPLoop 4
6117 typedef int CPKind;
6118
6119 typedef struct {
6120     CPKind kind;
6121     int pid;
6122     int fdTo, fdFrom;
6123 } ChildProc;
6124
6125
6126 int StartChildProcess(cmdLine, dir, pr)
6127      char *cmdLine;
6128      char *dir;
6129      ProcRef *pr;
6130 {
6131     char *argv[64], *p;
6132     int i, pid;
6133     int to_prog[2], from_prog[2];
6134     ChildProc *cp;
6135     char buf[MSG_SIZ];
6136
6137     if (appData.debugMode) {
6138         fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
6139     }
6140
6141     /* We do NOT feed the cmdLine to the shell; we just
6142        parse it into blank-separated arguments in the
6143        most simple-minded way possible.
6144        */
6145     i = 0;
6146     strcpy(buf, cmdLine);
6147     p = buf;
6148     for (;;) {
6149         argv[i++] = p;
6150         p = strchr(p, ' ');
6151         if (p == NULL) break;
6152         *p++ = NULLCHAR;
6153     }
6154     argv[i] = NULL;
6155
6156     SetUpChildIO(to_prog, from_prog);
6157
6158     #ifdef SIGWINCH
6159     signal(SIGWINCH, TermSizeSigHandler);
6160     #endif
6161
6162     if ((pid = fork()) == 0) {
6163         /* Child process */
6164         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
6165         close(to_prog[1]);     // first close the unused pipe ends
6166         close(from_prog[0]);
6167         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
6168         dup2(from_prog[1], 1);
6169         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
6170         close(from_prog[1]);                   // and closing again loses one of the pipes!
6171         if(fileno(stderr) >= 2) // better safe than sorry...
6172                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
6173
6174         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
6175             perror(dir);
6176             exit(1);
6177         }
6178
6179         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
6180
6181         execvp(argv[0], argv);
6182
6183         /* If we get here, exec failed */
6184         perror(argv[0]);
6185         exit(1);
6186     }
6187
6188     /* Parent process */
6189     close(to_prog[0]);
6190     close(from_prog[1]);
6191
6192     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
6193     cp->kind = CPReal;
6194     cp->pid = pid;
6195     cp->fdFrom = from_prog[0];
6196     cp->fdTo = to_prog[1];
6197     *pr = (ProcRef) cp;
6198     return 0;
6199 }
6200
6201 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
6202 static RETSIGTYPE AlarmCallBack(int n)
6203 {
6204     return;
6205 }
6206
6207 void
6208 DestroyChildProcess(pr, signalType)
6209      ProcRef pr;
6210      int signalType;
6211 {
6212     ChildProc *cp = (ChildProc *) pr;
6213
6214     if (cp->kind != CPReal) return;
6215     cp->kind = CPNone;
6216     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
6217         signal(SIGALRM, AlarmCallBack);
6218         alarm(3);
6219         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
6220             kill(cp->pid, SIGKILL); // kill it forcefully
6221             wait((int *) 0);        // and wait again
6222         }
6223     } else {
6224         if (signalType) {
6225             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
6226         }
6227         /* Process is exiting either because of the kill or because of
6228            a quit command sent by the backend; either way, wait for it to die.
6229         */
6230         wait((int *) 0);
6231     }
6232     close(cp->fdFrom);
6233     close(cp->fdTo);
6234 }
6235
6236 void
6237 InterruptChildProcess(pr)
6238      ProcRef pr;
6239 {
6240     ChildProc *cp = (ChildProc *) pr;
6241
6242     if (cp->kind != CPReal) return;
6243     (void) kill(cp->pid, SIGINT); /* stop it thinking */
6244 }
6245
6246 int OpenTelnet(host, port, pr)
6247      char *host;
6248      char *port;
6249      ProcRef *pr;
6250 {
6251     char cmdLine[MSG_SIZ];
6252
6253     if (port[0] == NULLCHAR) {
6254       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
6255     } else {
6256       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
6257     }
6258     return StartChildProcess(cmdLine, "", pr);
6259 }
6260
6261 int OpenTCP(host, port, pr)
6262      char *host;
6263      char *port;
6264      ProcRef *pr;
6265 {
6266 #if OMIT_SOCKETS
6267     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
6268 #else  /* !OMIT_SOCKETS */
6269     int s;
6270     struct sockaddr_in sa;
6271     struct hostent     *hp;
6272     unsigned short uport;
6273     ChildProc *cp;
6274
6275     if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
6276         return errno;
6277     }
6278
6279     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
6280     sa.sin_family = AF_INET;
6281     sa.sin_addr.s_addr = INADDR_ANY;
6282     uport = (unsigned short) 0;
6283     sa.sin_port = htons(uport);
6284     if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
6285         return errno;
6286     }
6287
6288     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
6289     if (!(hp = gethostbyname(host))) {
6290         int b0, b1, b2, b3;
6291         if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
6292             hp = (struct hostent *) calloc(1, sizeof(struct hostent));
6293             hp->h_addrtype = AF_INET;
6294             hp->h_length = 4;
6295             hp->h_addr_list = (char **) calloc(2, sizeof(char *));
6296             hp->h_addr_list[0] = (char *) malloc(4);
6297             hp->h_addr_list[0][0] = b0;
6298             hp->h_addr_list[0][1] = b1;
6299             hp->h_addr_list[0][2] = b2;
6300             hp->h_addr_list[0][3] = b3;
6301         } else {
6302             return ENOENT;
6303         }
6304     }
6305     sa.sin_family = hp->h_addrtype;
6306     uport = (unsigned short) atoi(port);
6307     sa.sin_port = htons(uport);
6308     memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
6309
6310     if (connect(s, (struct sockaddr *) &sa,
6311                 sizeof(struct sockaddr_in)) < 0) {
6312         return errno;
6313     }
6314
6315     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
6316     cp->kind = CPSock;
6317     cp->pid = 0;
6318     cp->fdFrom = s;
6319     cp->fdTo = s;
6320     *pr = (ProcRef) cp;
6321
6322 #endif /* !OMIT_SOCKETS */
6323
6324     return 0;
6325 }
6326
6327 int OpenCommPort(name, pr)
6328      char *name;
6329      ProcRef *pr;
6330 {
6331     int fd;
6332     ChildProc *cp;
6333
6334     fd = open(name, 2, 0);
6335     if (fd < 0) return errno;
6336
6337     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
6338     cp->kind = CPComm;
6339     cp->pid = 0;
6340     cp->fdFrom = fd;
6341     cp->fdTo = fd;
6342     *pr = (ProcRef) cp;
6343
6344     return 0;
6345 }
6346
6347 int OpenLoopback(pr)
6348      ProcRef *pr;
6349 {
6350     ChildProc *cp;
6351     int to[2], from[2];
6352
6353     SetUpChildIO(to, from);
6354
6355     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
6356     cp->kind = CPLoop;
6357     cp->pid = 0;
6358     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
6359     cp->fdTo = to[1];
6360     *pr = (ProcRef) cp;
6361
6362     return 0;
6363 }
6364
6365 int OpenRcmd(host, user, cmd, pr)
6366      char *host, *user, *cmd;
6367      ProcRef *pr;
6368 {
6369     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
6370     return -1;
6371 }
6372
6373 #define INPUT_SOURCE_BUF_SIZE 8192
6374
6375 typedef struct {
6376     CPKind kind;
6377     int fd;
6378     int lineByLine;
6379     char *unused;
6380     InputCallback func;
6381     guint sid;
6382     char buf[INPUT_SOURCE_BUF_SIZE];
6383     VOIDSTAR closure;
6384 } InputSource;
6385
6386 void
6387 DoInputCallback(io,cond,data)
6388      GIOChannel   *io;
6389      GIOCondition  cond;
6390      gpointer *data;
6391 {
6392   /* read input from one of the input source (for example a chess program, ICS, etc).
6393    * and call a function that will handle the input
6394    */
6395
6396   int count; /* how many bytes did we read */
6397   int error; 
6398   char *p, *q;
6399   
6400   /* All information (callback function, file descriptor, etc) is
6401    * saved in an InputSource structure 
6402    */
6403   InputSource *is = (InputSource *) data; 
6404   
6405   if (is->lineByLine) 
6406     {
6407       count = read(is->fd, is->unused,
6408                    INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
6409
6410       if (count <= 0) 
6411         {
6412           (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
6413           return;
6414         }
6415       is->unused += count;
6416       p = is->buf;
6417       /* break input into lines and call the callback function on each
6418        * line 
6419        */
6420       while (p < is->unused) 
6421         {
6422           q = memchr(p, '\n', is->unused - p);
6423           if (q == NULL) break;
6424           q++;
6425           (is->func)(is, is->closure, p, q - p, 0);
6426           p = q;
6427         }
6428       /* remember not yet used part of the buffer */
6429       q = is->buf;
6430       while (p < is->unused) 
6431         {
6432           *q++ = *p++;
6433         }
6434       is->unused = q;
6435     }
6436   else 
6437     {
6438       /* read maximum length of input buffer and send the whole buffer
6439        * to the callback function 
6440        */
6441       count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
6442       if (count == -1)
6443         error = errno;
6444       else
6445         error = 0;
6446       (is->func)(is, is->closure, is->buf, count, error);
6447     }
6448   
6449   return;
6450 }
6451
6452 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
6453      ProcRef pr;
6454      int lineByLine;
6455      InputCallback func;
6456      VOIDSTAR closure;
6457 {
6458     InputSource *is;
6459     GIOChannel *channel;
6460     ChildProc *cp = (ChildProc *) pr;
6461
6462     is = (InputSource *) calloc(1, sizeof(InputSource));
6463     is->lineByLine = lineByLine;
6464     is->func = func;
6465     if (pr == NoProc) {
6466         is->kind = CPReal;
6467         is->fd = fileno(stdin);
6468     } else {
6469         is->kind = cp->kind;
6470         is->fd = cp->fdFrom;
6471     }
6472     if (lineByLine) 
6473       is->unused = is->buf;
6474     else
6475       is->unused = NULL;
6476
6477 //    is->xid = XtAppAddInput(appContext, is->fd,
6478 //                          (XtPointer) (XtInputReadMask),
6479 //                          (XtInputCallbackProc) DoInputCallback,
6480 //                          (XtPointer) is);
6481 //
6482
6483     /* TODO: will this work on windows?*/
6484     printf("DEBUG: fd=%d %d\n",is->fd,is);
6485
6486     channel = g_io_channel_unix_new(is->fd);
6487     g_io_channel_set_close_on_unref (channel, TRUE);
6488     is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
6489     is->closure = closure;
6490     return (InputSourceRef) is;
6491 }
6492
6493 void
6494 RemoveInputSource(isr)
6495      InputSourceRef isr;
6496 {
6497     InputSource *is = (InputSource *) isr;
6498
6499     if (is->sid == 0) return;
6500     g_source_remove(is->sid);
6501     is->sid = 0;
6502     return;
6503 }
6504
6505 int OutputToProcess(pr, message, count, outError)
6506      ProcRef pr;
6507      char *message;
6508      int count;
6509      int *outError;
6510 {
6511     ChildProc *cp = (ChildProc *) pr;
6512     int outCount;
6513
6514     if (pr == NoProc)
6515       outCount = fwrite(message, 1, count, stdout);
6516     else
6517       outCount = write(cp->fdTo, message, count);
6518
6519     if (outCount == -1)
6520       *outError = errno;
6521     else
6522       *outError = 0;
6523
6524     return outCount;
6525 }
6526
6527 /* Output message to process, with "ms" milliseconds of delay
6528    between each character. This is needed when sending the logon
6529    script to ICC, which for some reason doesn't like the
6530    instantaneous send. */
6531 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
6532      ProcRef pr;
6533      char *message;
6534      int count;
6535      int *outError;
6536      long msdelay;
6537 {
6538     ChildProc *cp = (ChildProc *) pr;
6539     int outCount = 0;
6540     int r;
6541
6542     while (count--) {
6543         r = write(cp->fdTo, message++, 1);
6544         if (r == -1) {
6545             *outError = errno;
6546             return outCount;
6547         }
6548         ++outCount;
6549         if (msdelay >= 0)
6550           TimeDelay(msdelay);
6551     }
6552
6553     return outCount;
6554 }
6555
6556 /****   Animation code by Hugh Fisher, DCS, ANU.
6557
6558         Known problem: if a window overlapping the board is
6559         moved away while a piece is being animated underneath,
6560         the newly exposed area won't be updated properly.
6561         I can live with this.
6562
6563         Known problem: if you look carefully at the animation
6564         of pieces in mono mode, they are being drawn as solid
6565         shapes without interior detail while moving. Fixing
6566         this would be a major complication for minimal return.
6567 ****/
6568
6569 /*      Masks for XPM pieces. Black and white pieces can have
6570         different shapes, but in the interest of retaining my
6571         sanity pieces must have the same outline on both light
6572         and dark squares, and all pieces must use the same
6573         background square colors/images.                */
6574
6575 static int xpmDone = 0;
6576
6577 static void
6578 CreateAnimMasks (pieceDepth)
6579      int pieceDepth;
6580 {
6581   ChessSquare   piece;
6582   Pixmap        buf;
6583   GC            bufGC, maskGC;
6584   int           kind, n;
6585   unsigned long plane;
6586   XGCValues     values;
6587
6588   /* just return for gtk at the moment */
6589   return;
6590
6591   /* Need a bitmap just to get a GC with right depth */
6592   buf = XCreatePixmap(xDisplay, xBoardWindow,
6593                         8, 8, 1);
6594   values.foreground = 1;
6595   values.background = 0;
6596   /* Don't use XtGetGC, not read only */
6597   maskGC = XCreateGC(xDisplay, buf,
6598                     GCForeground | GCBackground, &values);
6599   XFreePixmap(xDisplay, buf);
6600
6601   buf = XCreatePixmap(xDisplay, xBoardWindow,
6602                       squareSize, squareSize, pieceDepth);
6603   values.foreground = XBlackPixel(xDisplay, xScreen);
6604   values.background = XWhitePixel(xDisplay, xScreen);
6605   bufGC = XCreateGC(xDisplay, buf,
6606                     GCForeground | GCBackground, &values);
6607
6608   for (piece = WhitePawn; piece <= BlackKing; piece++) {
6609     /* Begin with empty mask */
6610     if(!xpmDone) // [HGM] pieces: keep using existing
6611     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
6612                                  squareSize, squareSize, 1);
6613     XSetFunction(xDisplay, maskGC, GXclear);
6614     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
6615                    0, 0, squareSize, squareSize);
6616
6617     /* Take a copy of the piece */
6618     if (White(piece))
6619       kind = 0;
6620     else
6621       kind = 2;
6622     XSetFunction(xDisplay, bufGC, GXcopy);
6623     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
6624               buf, bufGC,
6625               0, 0, squareSize, squareSize, 0, 0);
6626
6627     /* XOR the background (light) over the piece */
6628     XSetFunction(xDisplay, bufGC, GXxor);
6629     if (useImageSqs)
6630       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
6631                 0, 0, squareSize, squareSize, 0, 0);
6632     else {
6633       XSetForeground(xDisplay, bufGC, lightSquareColor);
6634       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
6635     }
6636
6637     /* We now have an inverted piece image with the background
6638        erased. Construct mask by just selecting all the non-zero
6639        pixels - no need to reconstruct the original image.      */
6640     XSetFunction(xDisplay, maskGC, GXor);
6641     plane = 1;
6642     /* Might be quicker to download an XImage and create bitmap
6643        data from it rather than this N copies per piece, but it
6644        only takes a fraction of a second and there is a much
6645        longer delay for loading the pieces.             */
6646     for (n = 0; n < pieceDepth; n ++) {
6647       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
6648                  0, 0, squareSize, squareSize,
6649                  0, 0, plane);
6650       plane = plane << 1;
6651     }
6652   }
6653   /* Clean up */
6654   XFreePixmap(xDisplay, buf);
6655   XFreeGC(xDisplay, bufGC);
6656   XFreeGC(xDisplay, maskGC);
6657 }
6658
6659 static void
6660 InitAnimState (anim, info)
6661   AnimState * anim;
6662   XWindowAttributes * info;
6663 {
6664   XtGCMask  mask;
6665   XGCValues values;
6666
6667   /* Each buffer is square size, same depth as window */
6668 //  anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
6669 //                      squareSize, squareSize, info->depth);
6670 //  anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
6671 //                      squareSize, squareSize, info->depth);
6672 //
6673 //  /* Create a plain GC for blitting */
6674 //  mask = GCForeground | GCBackground | GCFunction |
6675 //         GCPlaneMask | GCGraphicsExposures;
6676 //  values.foreground = XBlackPixel(xDisplay, xScreen);
6677 //  values.background = XWhitePixel(xDisplay, xScreen);
6678 //  values.function   = GXcopy;
6679 //  values.plane_mask = AllPlanes;
6680 //  values.graphics_exposures = False;
6681 //  anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
6682 //
6683 //  /* Piece will be copied from an existing context at
6684 //     the start of each new animation/drag. */
6685 //  anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
6686 //
6687 //  /* Outline will be a read-only copy of an existing */
6688 //  anim->outlineGC = None;
6689 }
6690
6691 static void
6692 CreateAnimVars ()
6693 {
6694   static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
6695   XWindowAttributes info;
6696
6697   /* for gtk at the moment just ... */
6698   return;
6699
6700   if (xpmDone && gameInfo.variant == old) return;
6701   if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
6702   //  XGetWindowAttributes(xDisplay, xBoardWindow, &info);
6703
6704   //  InitAnimState(&game, &info);
6705   //  InitAnimState(&player, &info);
6706
6707   /* For XPM pieces, we need bitmaps to use as masks. */
6708   //  if (useImages)
6709   //    CreateAnimMasks(info.depth);
6710    xpmDone = 1;
6711 }
6712
6713 #ifndef HAVE_USLEEP
6714
6715 static Boolean frameWaiting;
6716
6717 static RETSIGTYPE FrameAlarm (sig)
6718      int sig;
6719 {
6720   frameWaiting = False;
6721   /* In case System-V style signals.  Needed?? */
6722   signal(SIGALRM, FrameAlarm);
6723 }
6724
6725 static void
6726 FrameDelay (time)
6727      int time;
6728 {
6729   struct itimerval delay;
6730
6731   XSync(xDisplay, False);
6732
6733   if (time > 0) {
6734     frameWaiting = True;
6735     signal(SIGALRM, FrameAlarm);
6736     delay.it_interval.tv_sec =
6737       delay.it_value.tv_sec = time / 1000;
6738     delay.it_interval.tv_usec =
6739       delay.it_value.tv_usec = (time % 1000) * 1000;
6740     setitimer(ITIMER_REAL, &delay, NULL);
6741     while (frameWaiting) pause();
6742     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
6743     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
6744     setitimer(ITIMER_REAL, &delay, NULL);
6745   }
6746 }
6747
6748 #else
6749
6750 static void
6751 FrameDelay (time)
6752      int time;
6753 {
6754   //  XSync(xDisplay, False);
6755   if (time > 0)
6756     usleep(time * 1000);
6757 }
6758
6759 #endif
6760
6761 /*      Convert board position to corner of screen rect and color       */
6762
6763 static void
6764 ScreenSquare(column, row, pt, color)
6765      int column; int row; XPoint * pt; int * color;
6766 {
6767   if (flipView) {
6768     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
6769     pt->y = lineGap + row * (squareSize + lineGap);
6770   } else {
6771     pt->x = lineGap + column * (squareSize + lineGap);
6772     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
6773   }
6774   *color = SquareColor(row, column);
6775 }
6776
6777 /*      Convert window coords to square                 */
6778
6779 static void
6780 BoardSquare(x, y, column, row)
6781      int x; int y; int * column; int * row;
6782 {
6783   *column = EventToSquare(x, BOARD_WIDTH);
6784   if (flipView && *column >= 0)
6785     *column = BOARD_WIDTH - 1 - *column;
6786   *row = EventToSquare(y, BOARD_HEIGHT);
6787   if (!flipView && *row >= 0)
6788     *row = BOARD_HEIGHT - 1 - *row;
6789 }
6790
6791 /*   Utilities  */
6792
6793 #undef Max  /* just in case */
6794 #undef Min
6795 #define Max(a, b) ((a) > (b) ? (a) : (b))
6796 #define Min(a, b) ((a) < (b) ? (a) : (b))
6797
6798 static void
6799 SetRect(rect, x, y, width, height)
6800      XRectangle * rect; int x; int y; int width; int height;
6801 {
6802   rect->x = x;
6803   rect->y = y;
6804   rect->width  = width;
6805   rect->height = height;
6806 }
6807
6808 /*      Test if two frames overlap. If they do, return
6809         intersection rect within old and location of
6810         that rect within new. */
6811
6812 static Boolean
6813 Intersect(old, new, size, area, pt)
6814      XPoint * old; XPoint * new;
6815      int size; XRectangle * area; XPoint * pt;
6816 {
6817   if (old->x > new->x + size || new->x > old->x + size ||
6818       old->y > new->y + size || new->y > old->y + size) {
6819     return False;
6820   } else {
6821     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
6822             size - abs(old->x - new->x), size - abs(old->y - new->y));
6823     pt->x = Max(old->x - new->x, 0);
6824     pt->y = Max(old->y - new->y, 0);
6825     return True;
6826   }
6827 }
6828
6829 /*      For two overlapping frames, return the rect(s)
6830         in the old that do not intersect with the new.   */
6831
6832 static void
6833 CalcUpdateRects(old, new, size, update, nUpdates)
6834      XPoint * old; XPoint * new; int size;
6835      XRectangle update[]; int * nUpdates;
6836 {
6837   int        count;
6838
6839   /* If old = new (shouldn't happen) then nothing to draw */
6840   if (old->x == new->x && old->y == new->y) {
6841     *nUpdates = 0;
6842     return;
6843   }
6844   /* Work out what bits overlap. Since we know the rects
6845      are the same size we don't need a full intersect calc. */
6846   count = 0;
6847   /* Top or bottom edge? */
6848   if (new->y > old->y) {
6849     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
6850     count ++;
6851   } else if (old->y > new->y) {
6852     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
6853                               size, old->y - new->y);
6854     count ++;
6855   }
6856   /* Left or right edge - don't overlap any update calculated above. */
6857   if (new->x > old->x) {
6858     SetRect(&(update[count]), old->x, Max(new->y, old->y),
6859                               new->x - old->x, size - abs(new->y - old->y));
6860     count ++;
6861   } else if (old->x > new->x) {
6862     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
6863                               old->x - new->x, size - abs(new->y - old->y));
6864     count ++;
6865   }
6866   /* Done */
6867   *nUpdates = count;
6868 }
6869
6870 /*      Generate a series of frame coords from start->mid->finish.
6871         The movement rate doubles until the half way point is
6872         reached, then halves back down to the final destination,
6873         which gives a nice slow in/out effect. The algorithmn
6874         may seem to generate too many intermediates for short
6875         moves, but remember that the purpose is to attract the
6876         viewers attention to the piece about to be moved and
6877         then to where it ends up. Too few frames would be less
6878         noticeable.                                             */
6879
6880 static void
6881 Tween(start, mid, finish, factor, frames, nFrames)
6882      XPoint * start; XPoint * mid;
6883      XPoint * finish; int factor;
6884      XPoint frames[]; int * nFrames;
6885 {
6886   int fraction, n, count;
6887
6888   count = 0;
6889
6890   /* Slow in, stepping 1/16th, then 1/8th, ... */
6891   fraction = 1;
6892   for (n = 0; n < factor; n++)
6893     fraction *= 2;
6894   for (n = 0; n < factor; n++) {
6895     frames[count].x = start->x + (mid->x - start->x) / fraction;
6896     frames[count].y = start->y + (mid->y - start->y) / fraction;
6897     count ++;
6898     fraction = fraction / 2;
6899   }
6900
6901   /* Midpoint */
6902   frames[count] = *mid;
6903   count ++;
6904
6905   /* Slow out, stepping 1/2, then 1/4, ... */
6906   fraction = 2;
6907   for (n = 0; n < factor; n++) {
6908     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
6909     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
6910     count ++;
6911     fraction = fraction * 2;
6912   }
6913   *nFrames = count;
6914 }
6915
6916 /*      Draw a piece on the screen without disturbing what's there      */
6917
6918 static void
6919 SelectGCMask(piece, clip, outline, mask)
6920      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
6921 {
6922   GC source;
6923
6924   /* Bitmap for piece being moved. */
6925   if (appData.monoMode) {
6926       *mask = *pieceToSolid(piece);
6927   } else if (useImages) {
6928 #if HAVE_LIBXPM
6929       *mask = xpmMask[piece];
6930 #else
6931       *mask = ximMaskPm[piece];
6932 #endif
6933   } else {
6934       *mask = *pieceToSolid(piece);
6935   }
6936
6937   /* GC for piece being moved. Square color doesn't matter, but
6938      since it gets modified we make a copy of the original. */
6939   if (White(piece)) {
6940     if (appData.monoMode)
6941       source = bwPieceGC;
6942     else
6943       source = wlPieceGC;
6944   } else {
6945     if (appData.monoMode)
6946       source = wbPieceGC;
6947     else
6948       source = blPieceGC;
6949   }
6950   //  XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
6951
6952   /* Outline only used in mono mode and is not modified */
6953   if (White(piece))
6954     *outline = bwPieceGC;
6955   else
6956     *outline = wbPieceGC;
6957 }
6958
6959 static void
6960 OverlayPiece(piece, clip, outline,  dest)
6961      ChessSquare piece; GC clip; GC outline; Drawable dest;
6962 {
6963   int   kind;
6964
6965   if (!useImages) {
6966     /* Draw solid rectangle which will be clipped to shape of piece */
6967 //    XFillRectangle(xDisplay, dest, clip,
6968 //                 0, 0, squareSize, squareSize)
6969 ;
6970     if (appData.monoMode)
6971       /* Also draw outline in contrasting color for black
6972          on black / white on white cases                */
6973 //      XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
6974 //               0, 0, squareSize, squareSize, 0, 0, 1)
6975 ;
6976   } else {
6977     /* Copy the piece */
6978     if (White(piece))
6979       kind = 0;
6980     else
6981       kind = 2;
6982 //    XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
6983 //            dest, clip,
6984 //            0, 0, squareSize, squareSize,
6985 //            0, 0);
6986   }
6987 }
6988
6989 /* Animate the movement of a single piece */
6990
6991 static void
6992 BeginAnimation(anim, piece, startColor, start)
6993      AnimState *anim;
6994      ChessSquare piece;
6995      int startColor;
6996      XPoint * start;
6997 {
6998   Pixmap mask;
6999
7000   /* The old buffer is initialised with the start square (empty) */
7001   BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
7002   anim->prevFrame = *start;
7003
7004   /* The piece will be drawn using its own bitmap as a matte    */
7005 //  SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
7006 //  XSetClipMask(xDisplay, anim->pieceGC, mask);
7007 }
7008
7009 static void
7010 AnimationFrame(anim, frame, piece)
7011      AnimState *anim;
7012      XPoint *frame;
7013      ChessSquare piece;
7014 {
7015   XRectangle updates[4];
7016   XRectangle overlap;
7017   XPoint     pt;
7018   int        count, i;
7019
7020   /* Save what we are about to draw into the new buffer */
7021 //  XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
7022 //          frame->x, frame->y, squareSize, squareSize,
7023 //          0, 0);
7024
7025   /* Erase bits of the previous frame */
7026   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
7027     /* Where the new frame overlapped the previous,
7028        the contents in newBuf are wrong. */
7029 //    XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
7030 //            overlap.x, overlap.y,
7031 //            overlap.width, overlap.height,
7032 //            pt.x, pt.y);
7033     /* Repaint the areas in the old that don't overlap new */
7034     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
7035     for (i = 0; i < count; i++)
7036 //      XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
7037 //              updates[i].x - anim->prevFrame.x,
7038 //              updates[i].y - anim->prevFrame.y,
7039 //              updates[i].width, updates[i].height,
7040 //              updates[i].x, updates[i].y)
7041 ;
7042   } else {
7043     /* Easy when no overlap */
7044 //    XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
7045 //                0, 0, squareSize, squareSize,
7046 //                anim->prevFrame.x, anim->prevFrame.y);
7047   }
7048
7049   /* Save this frame for next time round */
7050 //  XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
7051 //              0, 0, squareSize, squareSize,
7052 //              0, 0);
7053   anim->prevFrame = *frame;
7054
7055   /* Draw piece over original screen contents, not current,
7056      and copy entire rect. Wipes out overlapping piece images. */
7057   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
7058 //  XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
7059 //              0, 0, squareSize, squareSize,
7060 //              frame->x, frame->y);
7061 }
7062
7063 static void
7064 EndAnimation (anim, finish)
7065      AnimState *anim;
7066      XPoint *finish;
7067 {
7068   XRectangle updates[4];
7069   XRectangle overlap;
7070   XPoint     pt;
7071   int        count, i;
7072
7073   /* The main code will redraw the final square, so we
7074      only need to erase the bits that don't overlap.    */
7075   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
7076     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
7077     for (i = 0; i < count; i++)
7078 //      XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
7079 //              updates[i].x - anim->prevFrame.x,
7080 //              updates[i].y - anim->prevFrame.y,
7081 //              updates[i].width, updates[i].height,
7082 //              updates[i].x, updates[i].y)
7083 ;
7084   } else {
7085 //    XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
7086 //              0, 0, squareSize, squareSize,
7087 //              anim->prevFrame.x, anim->prevFrame.y);
7088   }
7089 }
7090
7091 static void
7092 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
7093      AnimState *anim;
7094      ChessSquare piece; int startColor;
7095      XPoint * start; XPoint * finish;
7096      XPoint frames[]; int nFrames;
7097 {
7098   int n;
7099
7100   BeginAnimation(anim, piece, startColor, start);
7101   for (n = 0; n < nFrames; n++) {
7102     AnimationFrame(anim, &(frames[n]), piece);
7103     FrameDelay(appData.animSpeed);
7104   }
7105   EndAnimation(anim, finish);
7106 }
7107
7108 /* Main control logic for deciding what to animate and how */
7109
7110 void
7111 AnimateMove(board, fromX, fromY, toX, toY)
7112      Board board;
7113      int fromX;
7114      int fromY;
7115      int toX;
7116      int toY;
7117 {
7118   ChessSquare piece;
7119   int hop;
7120   XPoint      start, finish, mid;
7121   XPoint      frames[kFactor * 2 + 1];
7122   int         nFrames, startColor, endColor;
7123
7124   /* Are we animating? */
7125   if (!appData.animate || appData.blindfold)
7126     return;
7127
7128   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
7129      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
7130         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
7131
7132   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
7133   piece = board[fromY][fromX];
7134   if (piece >= EmptySquare) return;
7135
7136 #if DONT_HOP
7137   hop = FALSE;
7138 #else
7139   hop = (piece == WhiteKnight || piece == BlackKnight);
7140 #endif
7141
7142   if (appData.debugMode) {
7143       fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
7144                              _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
7145              piece, fromX, fromY, toX, toY);  }
7146
7147   ScreenSquare(fromX, fromY, &start, &startColor);
7148   ScreenSquare(toX, toY, &finish, &endColor);
7149
7150   if (hop) {
7151     /* Knight: make diagonal movement then straight */
7152     if (abs(toY - fromY) < abs(toX - fromX)) {
7153        mid.x = start.x + (finish.x - start.x) / 2;
7154        mid.y = finish.y;
7155      } else {
7156        mid.x = finish.x;
7157        mid.y = start.y + (finish.y - start.y) / 2;
7158      }
7159   } else {
7160     mid.x = start.x + (finish.x - start.x) / 2;
7161     mid.y = start.y + (finish.y - start.y) / 2;
7162   }
7163
7164   /* Don't use as many frames for very short moves */
7165   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
7166     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
7167   else
7168     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
7169   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
7170
7171   /* Be sure end square is redrawn */
7172   damage[toY][toX] = True;
7173 }
7174
7175 void
7176 DragPieceBegin(x, y)
7177      int x; int y;
7178 {
7179     int  boardX, boardY, color;
7180     XPoint corner;
7181
7182     /* Are we animating? */
7183     if (!appData.animateDragging || appData.blindfold)
7184       return;
7185
7186     /* Figure out which square we start in and the
7187        mouse position relative to top left corner. */
7188     BoardSquare(x, y, &boardX, &boardY);
7189     player.startBoardX = boardX;
7190     player.startBoardY = boardY;
7191     ScreenSquare(boardX, boardY, &corner, &color);
7192     player.startSquare  = corner;
7193     player.startColor   = color;
7194     /* As soon as we start dragging, the piece will jump slightly to
7195        be centered over the mouse pointer. */
7196     player.mouseDelta.x = squareSize/2;
7197     player.mouseDelta.y = squareSize/2;
7198     /* Initialise animation */
7199     player.dragPiece = PieceForSquare(boardX, boardY);
7200     /* Sanity check */
7201     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
7202         player.dragActive = True;
7203         BeginAnimation(&player, player.dragPiece, color, &corner);
7204         /* Mark this square as needing to be redrawn. Note that
7205            we don't remove the piece though, since logically (ie
7206            as seen by opponent) the move hasn't been made yet. */
7207            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
7208               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
7209 //           XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
7210 //                   corner.x, corner.y, squareSize, squareSize,
7211 //                   0, 0); // [HGM] zh: unstack in stead of grab
7212         damage[boardY][boardX] = True;
7213     } else {
7214         player.dragActive = False;
7215     }
7216 }
7217
7218 static void
7219 DragPieceMove(x, y)
7220      int x; int y;
7221 {
7222     XPoint corner;
7223
7224     /* Are we animating? */
7225     if (!appData.animateDragging || appData.blindfold)
7226       return;
7227
7228     /* Sanity check */
7229     if (! player.dragActive)
7230       return;
7231     /* Move piece, maintaining same relative position
7232        of mouse within square    */
7233     corner.x = x - player.mouseDelta.x;
7234     corner.y = y - player.mouseDelta.y;
7235     AnimationFrame(&player, &corner, player.dragPiece);
7236 #if HIGHDRAG
7237     if (appData.highlightDragging) {
7238         int boardX, boardY;
7239         BoardSquare(x, y, &boardX, &boardY);
7240         SetHighlights(fromX, fromY, boardX, boardY);
7241     }
7242 #endif
7243 }
7244
7245 void
7246 DragPieceEnd(x, y)
7247      int x; int y;
7248 {
7249     int boardX, boardY, color;
7250     XPoint corner;
7251
7252     /* Are we animating? */
7253     if (!appData.animateDragging || appData.blindfold)
7254       return;
7255
7256     /* Sanity check */
7257     if (! player.dragActive)
7258       return;
7259     /* Last frame in sequence is square piece is
7260        placed on, which may not match mouse exactly. */
7261     BoardSquare(x, y, &boardX, &boardY);
7262     ScreenSquare(boardX, boardY, &corner, &color);
7263     EndAnimation(&player, &corner);
7264
7265     /* Be sure end square is redrawn */
7266     damage[boardY][boardX] = True;
7267
7268     /* This prevents weird things happening with fast successive
7269        clicks which on my Sun at least can cause motion events
7270        without corresponding press/release. */
7271     player.dragActive = False;
7272 }
7273
7274 /* Handle expose event while piece being dragged */
7275
7276 static void
7277 DrawDragPiece ()
7278 {
7279   if (!player.dragActive || appData.blindfold)
7280     return;
7281
7282   /* What we're doing: logically, the move hasn't been made yet,
7283      so the piece is still in it's original square. But visually
7284      it's being dragged around the board. So we erase the square
7285      that the piece is on and draw it at the last known drag point. */
7286   BlankSquare(player.startSquare.x, player.startSquare.y,
7287                 player.startColor, EmptySquare, xBoardWindow);
7288   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
7289   damage[player.startBoardY][player.startBoardX] = TRUE;
7290 }
7291
7292 void
7293 SetProgramStats( FrontEndProgramStats * stats )
7294 {
7295   // [HR] TODO
7296   // [HGM] done, but perhaps backend should call this directly?
7297     EngineOutputUpdate( stats );
7298 }
7299
7300 #include <sys/ioctl.h>
7301 int get_term_width()
7302 {
7303     int fd, default_width;
7304
7305     fd = STDIN_FILENO;
7306     default_width = 79; // this is FICS default anyway...
7307
7308 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
7309     struct ttysize win;
7310     if (!ioctl(fd, TIOCGSIZE, &win))
7311         default_width = win.ts_cols;
7312 #elif defined(TIOCGWINSZ)
7313     struct winsize win;
7314     if (!ioctl(fd, TIOCGWINSZ, &win))
7315         default_width = win.ws_col;
7316 #endif
7317     return default_width;
7318 }
7319
7320 void update_ics_width()
7321 {
7322     static int old_width = 0;
7323     int new_width = get_term_width();
7324
7325     if (old_width != new_width)
7326        ics_printf("set width %d\n", new_width);
7327     old_width = new_width;
7328 }
7329
7330 void NotifyFrontendLogin()
7331 {
7332     update_ics_width();
7333 }