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