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