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