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