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