fixed loading games. crashed because a X-func was called.
[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_Aspect = GTK_WIDGET (gtk_builder_get_object (builder, "Aspectframe"));
2202     if(!GUI_Aspect) printf("Error: gtk_builder didn't work!\n");
2203
2204     GUI_History = GTK_WIDGET (gtk_builder_get_object (builder, "MoveHistory"));
2205     if(!GUI_History) printf("Error: gtk_builder didn't work!\n");
2206
2207     GUI_Menubar  = GTK_WIDGET (gtk_builder_get_object (builder, "MenuBar"));
2208     if(!GUI_Menubar) printf("Error: gtk_builder didn't work!\n");
2209     GUI_Timer  = GTK_WIDGET (gtk_builder_get_object (builder, "Timer"));
2210     if(!GUI_Timer) printf("Error: gtk_builder didn't work!\n");
2211     GUI_Buttonbar  = GTK_WIDGET (gtk_builder_get_object (builder, "ButtonBar"));
2212     if(!GUI_Buttonbar) printf("Error: gtk_builder didn't work!\n");
2213     GUI_Board  = GTK_WIDGET (gtk_builder_get_object (builder, "Board"));
2214     if(!GUI_Board) printf("Error: gtk_builder didn't work!\n");
2215
2216     GUI_Whiteclock  = GTK_WIDGET (gtk_builder_get_object (builder, "WhiteClock"));
2217     if(!GUI_Whiteclock) printf("Error: gtk_builder didn't work!\n");
2218
2219     GUI_Blackclock  = GTK_WIDGET (gtk_builder_get_object (builder, "BlackClock"));
2220     if(!GUI_Blackclock) printf("Error: gtk_builder didn't work!\n");
2221
2222     LIST_MoveHistory = GTK_LIST_STORE (gtk_builder_get_object (builder, "MoveHistoryStore"));
2223     if(!LIST_MoveHistory) printf("Error: gtk_builder didn't work!\n");
2224
2225
2226     gtk_builder_connect_signals (builder, NULL);
2227
2228     // don't unref the builder, since we use it to get references to widgets
2229     //    g_object_unref (G_OBJECT (builder));
2230
2231     /* end parse glade file */
2232
2233     if (argc > 1)
2234       {
2235         fprintf(stderr, _("%s: unrecognized argument %s\n"),
2236                 programName, argv[1]);
2237
2238         fprintf(stderr, "Recognized options:\n");
2239         for(i = 0; i < XtNumber(shellOptions); i++) 
2240           {
2241             /* print first column */
2242             j = fprintf(stderr, "  %s%s", shellOptions[i].option,
2243                         (shellOptions[i].argKind == XrmoptionSepArg
2244                          ? " ARG" : ""));
2245             /* print second column and end line */
2246             if (++i < XtNumber(shellOptions)) 
2247               {         
2248                 fprintf(stderr, "%*c%s%s\n", 40 - j, ' ',
2249                         shellOptions[i].option,
2250                         (shellOptions[i].argKind == XrmoptionSepArg
2251                          ? " ARG" : ""));
2252               } 
2253             else 
2254               {
2255                 fprintf(stderr, "\n");
2256               };
2257           };
2258         exit(2);
2259       };
2260
2261     p = getenv("HOME");
2262     if (p == NULL) p = "/tmp";
2263     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
2264     gameCopyFilename = (char*) malloc(i);
2265     gamePasteFilename = (char*) malloc(i);
2266     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
2267     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2268
2269     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2270                               clientResources, XtNumber(clientResources),
2271                               NULL, 0);
2272
2273     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2274         static char buf[MSG_SIZ];
2275         EscapeExpand(buf, appData.initString);
2276         appData.initString = strdup(buf);
2277         EscapeExpand(buf, appData.secondInitString);
2278         appData.secondInitString = strdup(buf);
2279         EscapeExpand(buf, appData.firstComputerString);
2280         appData.firstComputerString = strdup(buf);
2281         EscapeExpand(buf, appData.secondComputerString);
2282         appData.secondComputerString = strdup(buf);
2283     }
2284
2285     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2286         chessDir = ".";
2287     } else {
2288         if (chdir(chessDir) != 0) {
2289             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2290             perror(chessDir);
2291             exit(1);
2292         }
2293     }
2294
2295     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2296         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2297         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
2298            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2299            exit(errno);
2300         }
2301         setbuf(debugFP, NULL);
2302     }
2303
2304     /* [HGM,HR] make sure board size is acceptable */
2305     if(appData.NrFiles > BOARD_SIZE ||
2306        appData.NrRanks > BOARD_SIZE   )
2307       DisplayFatalError(_("Recompile with BOARD_SIZE > 12, to support this size"), 0, 2);
2308
2309 #if !HIGHDRAG
2310     /* This feature does not work; animation needs a rewrite */
2311     appData.highlightDragging = FALSE;
2312 #endif
2313     InitBackEnd1();
2314
2315     xDisplay = XtDisplay(shellWidget);
2316     xScreen = DefaultScreen(xDisplay);
2317     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2318
2319     gameInfo.variant = StringToVariant(appData.variant);
2320     InitPosition(FALSE);
2321
2322     /* calc board size */
2323     if (isdigit(appData.boardSize[0])) 
2324       {
2325         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2326                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2327                    &fontPxlSize, &smallLayout, &tinyLayout);
2328         if (i == 0) 
2329           {
2330             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2331                     programName, appData.boardSize);
2332             exit(2);
2333           }
2334         if (i < 7) 
2335           {
2336             /* Find some defaults; use the nearest known size */
2337             SizeDefaults *szd, *nearest;
2338             int distance = 99999;
2339             nearest = szd = sizeDefaults;
2340             while (szd->name != NULL) 
2341               {
2342                 if (abs(szd->squareSize - squareSize) < distance) 
2343                   {
2344                     nearest = szd;
2345                     distance = abs(szd->squareSize - squareSize);
2346                     if (distance == 0) break;
2347                   }
2348                 szd++;
2349               };
2350             if (i < 2) lineGap = nearest->lineGap;
2351             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2352             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2353             if (i < 5) fontPxlSize = nearest->fontPxlSize;
2354             if (i < 6) smallLayout = nearest->smallLayout;
2355             if (i < 7) tinyLayout = nearest->tinyLayout;
2356           }
2357       } 
2358     else 
2359       {
2360         SizeDefaults *szd = sizeDefaults;
2361         if (*appData.boardSize == NULLCHAR) 
2362           {
2363             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize 
2364                    || DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) 
2365               {
2366                 szd++;
2367               }
2368             if (szd->name == NULL) szd--;
2369           } 
2370         else 
2371           {
2372             while (szd->name != NULL 
2373                    && StrCaseCmp(szd->name, appData.boardSize) != 0) 
2374               szd++;
2375             if (szd->name == NULL) 
2376               {
2377                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2378                         programName, appData.boardSize);
2379                 exit(2);
2380               }
2381           }
2382         squareSize = szd->squareSize;
2383         lineGap = szd->lineGap;
2384         clockFontPxlSize = szd->clockFontPxlSize;
2385         coordFontPxlSize = szd->coordFontPxlSize;
2386         fontPxlSize = szd->fontPxlSize;
2387         smallLayout = szd->smallLayout;
2388         tinyLayout = szd->tinyLayout;
2389       }
2390     /* end figuring out what size to use */
2391     
2392     boardWidth  = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2393     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2394     
2395     /*
2396      * Determine what fonts to use.
2397      */
2398     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2399     clockFontID = XLoadFont(xDisplay, appData.clockFont);
2400     clockFontStruct = XQueryFont(xDisplay, clockFontID);
2401     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2402     coordFontID = XLoadFont(xDisplay, appData.coordFont);
2403     coordFontStruct = XQueryFont(xDisplay, coordFontID);
2404     appData.font = FindFont(appData.font, fontPxlSize);
2405     countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
2406     countFontStruct = XQueryFont(xDisplay, countFontID);
2407 //    appData.font = FindFont(appData.font, fontPxlSize);
2408
2409     xdb = XtDatabase(xDisplay);
2410     XrmPutStringResource(&xdb, "*font", appData.font);
2411
2412     /*
2413      * Detect if there are not enough colors available and adapt.
2414      */
2415     if (DefaultDepth(xDisplay, xScreen) <= 2) {
2416       appData.monoMode = True;
2417     }
2418
2419     if (!appData.monoMode) {
2420         vFrom.addr = (caddr_t) appData.lightSquareColor;
2421         vFrom.size = strlen(appData.lightSquareColor);
2422         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2423         if (vTo.addr == NULL) {
2424           appData.monoMode = True;
2425           forceMono = True;
2426         } else {
2427           lightSquareColor = *(Pixel *) vTo.addr;
2428         }
2429     }
2430     if (!appData.monoMode) {
2431         vFrom.addr = (caddr_t) appData.darkSquareColor;
2432         vFrom.size = strlen(appData.darkSquareColor);
2433         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2434         if (vTo.addr == NULL) {
2435           appData.monoMode = True;
2436           forceMono = True;
2437         } else {
2438           darkSquareColor = *(Pixel *) vTo.addr;
2439         }
2440     }
2441     if (!appData.monoMode) {
2442         vFrom.addr = (caddr_t) appData.whitePieceColor;
2443         vFrom.size = strlen(appData.whitePieceColor);
2444         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2445         if (vTo.addr == NULL) {
2446           appData.monoMode = True;
2447           forceMono = True;
2448         } else {
2449           whitePieceColor = *(Pixel *) vTo.addr;
2450         }
2451     }
2452     if (!appData.monoMode) {
2453         vFrom.addr = (caddr_t) appData.blackPieceColor;
2454         vFrom.size = strlen(appData.blackPieceColor);
2455         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2456         if (vTo.addr == NULL) {
2457           appData.monoMode = True;
2458           forceMono = True;
2459         } else {
2460           blackPieceColor = *(Pixel *) vTo.addr;
2461         }
2462     }
2463
2464     if (!appData.monoMode) {
2465         vFrom.addr = (caddr_t) appData.highlightSquareColor;
2466         vFrom.size = strlen(appData.highlightSquareColor);
2467         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2468         if (vTo.addr == NULL) {
2469           appData.monoMode = True;
2470           forceMono = True;
2471         } else {
2472           highlightSquareColor = *(Pixel *) vTo.addr;
2473         }
2474     }
2475
2476     if (!appData.monoMode) {
2477         vFrom.addr = (caddr_t) appData.premoveHighlightColor;
2478         vFrom.size = strlen(appData.premoveHighlightColor);
2479         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2480         if (vTo.addr == NULL) {
2481           appData.monoMode = True;
2482           forceMono = True;
2483         } else {
2484           premoveHighlightColor = *(Pixel *) vTo.addr;
2485         }
2486     }
2487
2488     if (forceMono) {
2489       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2490               programName);
2491
2492       if (appData.bitmapDirectory == NULL ||
2493               appData.bitmapDirectory[0] == NULLCHAR)
2494             appData.bitmapDirectory = DEF_BITMAP_DIR;
2495     }
2496
2497     if (appData.lowTimeWarning && !appData.monoMode) {
2498       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2499       vFrom.size = strlen(appData.lowTimeWarningColor);
2500       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2501       if (vTo.addr == NULL)
2502                 appData.monoMode = True;
2503       else
2504                 lowTimeWarningColor = *(Pixel *) vTo.addr;
2505     }
2506
2507     if (appData.monoMode && appData.debugMode) {
2508         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2509                 (unsigned long) XWhitePixel(xDisplay, xScreen),
2510                 (unsigned long) XBlackPixel(xDisplay, xScreen));
2511     }
2512
2513     if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
2514         parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
2515         parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
2516         parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
2517         parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
2518         parse_cpair(ColorTell, appData.colorTell) < 0 ||
2519         parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
2520         parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
2521         parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
2522         parse_cpair(ColorNormal, appData.colorNormal) < 0)
2523       {
2524           if (appData.colorize) {
2525               fprintf(stderr,
2526                       _("%s: can't parse color names; disabling colorization\n"),
2527                       programName);
2528           }
2529           appData.colorize = FALSE;
2530       }
2531     textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2532     textColors[ColorNone].attr = 0;
2533
2534     //    XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2535
2536     /*
2537      * widget hierarchy
2538      */
2539     if (tinyLayout) {
2540         layoutName = "tinyLayout";
2541     } else if (smallLayout) {
2542         layoutName = "smallLayout";
2543     } else {
2544         layoutName = "normalLayout";
2545     }
2546
2547     if (appData.titleInWindow) {
2548       /* todo check what this appdata does */
2549     }
2550
2551     if (appData.showButtonBar) {
2552       /* TODO hide button bar if requested */
2553     }
2554
2555
2556     if (appData.titleInWindow)
2557       {
2558         if (smallLayout)
2559           {
2560             /* make it small */
2561             if (appData.showButtonBar)
2562               {
2563
2564               }
2565           }
2566         else
2567           {
2568             if (appData.showButtonBar)
2569               {
2570               }
2571           }
2572       }
2573     else
2574       {
2575       }
2576
2577
2578     /* set some checkboxes in the menu according to appData */
2579
2580     if (appData.alwaysPromoteToQueen)
2581       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Always Queen")),TRUE);
2582
2583     if (appData.animateDragging)
2584       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Dragging")),TRUE);
2585
2586     if (appData.animate)
2587       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Moving")),TRUE);
2588
2589     if (appData.autoComment)
2590       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Comment")),TRUE);
2591
2592     if (appData.autoCallFlag)
2593       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flag")),TRUE);
2594
2595     if (appData.autoFlipView)
2596       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flip View")),TRUE);
2597
2598     if (appData.autoObserve)
2599       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Observe")),TRUE);
2600
2601     if (appData.autoRaiseBoard)
2602       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Raise Board")),TRUE);
2603
2604     if (appData.autoSaveGames)
2605       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
2606
2607     if (appData.saveGameFile[0] != NULLCHAR)
2608       {
2609         /* Can't turn this off from menu */
2610         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
2611         gtk_action_set_sensitive(GTK_ACTION (gtk_builder_get_object (builder, "menuOptions.Auto Save")),FALSE);
2612       }
2613
2614     if (appData.blindfold)
2615       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Blindfold")),TRUE);
2616
2617     if (appData.flashCount > 0)
2618       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Flash Moves")),TRUE);
2619
2620     if (appData.getMoveList)
2621       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Get Move List")),TRUE);
2622
2623 #if HIGHDRAG
2624     if (appData.highlightDragging)
2625       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Dragging")),TRUE);
2626 #endif
2627
2628     if (appData.highlightLastMove)
2629       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Last Move")),TRUE);
2630
2631     if (appData.icsAlarm)
2632       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.ICS Alarm")),TRUE);
2633
2634     if (appData.ringBellAfterMoves)
2635       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Move Sound")),TRUE);
2636
2637     if (appData.oldSaveStyle)
2638       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Old Save Style")),TRUE);
2639
2640     if (appData.periodicUpdates)
2641       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Periodic Updates")),TRUE);
2642
2643     if (appData.ponderNextMove)
2644       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Ponder Next Move")),TRUE);
2645
2646     if (appData.popupExitMessage)
2647       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Exit Message")),TRUE);
2648
2649     if (appData.popupMoveErrors)
2650       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Move Errors")),TRUE);
2651
2652     if (appData.premove)
2653       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Premove")),TRUE);
2654
2655     if (appData.quietPlay)
2656       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Quit Play")),TRUE);
2657
2658     if (appData.showCoords)
2659       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Show Coords")),TRUE);
2660
2661     if (appData.showThinking)
2662       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Show Thinking")),TRUE);
2663
2664     if (appData.testLegality)
2665       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Test Legality")),TRUE);
2666
2667     /* end setting check boxes */
2668
2669     /* load square colors */
2670     SVGLightSquare   = load_pixbuf("svg/LightSquare.svg",squareSize);
2671     SVGDarkSquare    = load_pixbuf("svg/DarkSquare.svg",squareSize);
2672     SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2673
2674     /* use two icons to indicate if it is white's or black's turn */
2675     WhiteIcon  = load_pixbuf("svg/icon_white.svg",0);
2676     BlackIcon  = load_pixbuf("svg/icon_black.svg",0);
2677     WindowIcon = WhiteIcon;
2678     gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
2679
2680
2681     /* realize window */
2682     gtk_widget_show (GUI_Window);
2683
2684     /* recalc boardsize */
2685     CreateGCs();
2686     CreatePieces();
2687     CreatePieceMenus();
2688
2689     if (appData.animate || appData.animateDragging)
2690       CreateAnimVars();
2691
2692     InitBackEnd2();
2693
2694     if (errorExitStatus == -1) {
2695         if (appData.icsActive) {
2696             /* We now wait until we see "login:" from the ICS before
2697                sending the logon script (problems with timestamp otherwise) */
2698             /*ICSInitScript();*/
2699             if (appData.icsInputBox) ICSInputBoxPopUp();
2700         }
2701
2702         signal(SIGINT, IntSigHandler);
2703         signal(SIGTERM, IntSigHandler);
2704         if (*appData.cmailGameName != NULLCHAR) {
2705             signal(SIGUSR1, CmailSigHandler);
2706         }
2707     }
2708     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2709     InitPosition(TRUE);
2710
2711     /*
2712      * Create a cursor for the board widget.
2713      * (This needs to be called after the window has been created to have access to board-window)
2714      */
2715
2716     BoardCursor = gdk_cursor_new(GDK_HAND2);
2717     gdk_window_set_cursor(GUI_Board->window, BoardCursor);
2718     gdk_cursor_destroy(BoardCursor);
2719
2720     /* end cursor */
2721     gtk_main ();
2722
2723     if (appData.debugMode) fclose(debugFP); // [DM] debug
2724     return 0;
2725 }
2726
2727 void
2728 ShutDownFrontEnd()
2729 {
2730     if (appData.icsActive && oldICSInteractionTitle != NULL) {
2731         DisplayIcsInteractionTitle(oldICSInteractionTitle);
2732     }
2733     unlink(gameCopyFilename);
2734     unlink(gamePasteFilename);
2735 }
2736
2737 RETSIGTYPE TermSizeSigHandler(int sig)
2738 {
2739     update_ics_width();
2740 }
2741
2742 RETSIGTYPE
2743 IntSigHandler(sig)
2744      int sig;
2745 {
2746     ExitEvent(sig);
2747 }
2748
2749 RETSIGTYPE
2750 CmailSigHandler(sig)
2751      int sig;
2752 {
2753     int dummy = 0;
2754     int error;
2755
2756     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
2757
2758     /* Activate call-back function CmailSigHandlerCallBack()             */
2759     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2760
2761     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2762 }
2763
2764 void
2765 CmailSigHandlerCallBack(isr, closure, message, count, error)
2766      InputSourceRef isr;
2767      VOIDSTAR closure;
2768      char *message;
2769      int count;
2770      int error;
2771 {
2772     BoardToTop();
2773     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
2774 }
2775 /**** end signal code ****/
2776
2777
2778 void
2779 ICSInitScript()
2780 {
2781     FILE *f;
2782     char buf[MSG_SIZ];
2783     char *p;
2784
2785     f = fopen(appData.icsLogon, "r");
2786     if (f == NULL) {
2787         p = getenv("HOME");
2788         if (p != NULL) {
2789             strcpy(buf, p);
2790             strcat(buf, "/");
2791             strcat(buf, appData.icsLogon);
2792             f = fopen(buf, "r");
2793         }
2794     }
2795     if (f != NULL)
2796       ProcessICSInitScript(f);
2797 }
2798
2799 void
2800 ResetFrontEnd()
2801 {
2802     CommentPopDown();
2803     EditCommentPopDown();
2804     TagsPopDown();
2805     return;
2806 }
2807
2808 void
2809 SetMenuEnables(enab)
2810      Enables *enab;
2811 {
2812   GObject *o;
2813
2814   if (!builder) return;
2815   while (enab->name != NULL) {
2816     o = gtk_builder_get_object(builder, enab->name);
2817     if(GTK_IS_WIDGET(o))
2818       gtk_widget_set_sensitive(GTK_WIDGET (o),enab->value);
2819     else
2820       {
2821         if(GTK_IS_ACTION(o))
2822           gtk_action_set_sensitive(GTK_ACTION (o),enab->value);
2823         else
2824           DisplayError(enab->name, 0);
2825       }
2826     enab++;
2827   }
2828 }
2829
2830 void SetICSMode()
2831 {
2832   SetMenuEnables(icsEnables);
2833
2834 #ifdef ZIPPY
2835   if (appData.zippyPlay && !appData.noChessProgram)   /* [DM] icsEngineAnalyze */
2836     {}; //     XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2837 #endif
2838 }
2839
2840 void
2841 SetNCPMode()
2842 {
2843   SetMenuEnables(ncpEnables);
2844 }
2845
2846 void
2847 SetGNUMode()
2848 {
2849   SetMenuEnables(gnuEnables);
2850 }
2851
2852 void
2853 SetCmailMode()
2854 {
2855   SetMenuEnables(cmailEnables);
2856 }
2857
2858 void
2859 SetTrainingModeOn()
2860 {
2861   SetMenuEnables(trainingOnEnables);
2862   if (appData.showButtonBar) {
2863     //    XtSetSensitive(buttonBarWidget, False);
2864   }
2865   CommentPopDown();
2866 }
2867
2868 void
2869 SetTrainingModeOff()
2870 {
2871   SetMenuEnables(trainingOffEnables);
2872   if (appData.showButtonBar) {
2873     //    XtSetSensitive(buttonBarWidget, True);
2874   }
2875 }
2876
2877 void
2878 SetUserThinkingEnables()
2879 {
2880   if (appData.noChessProgram) return;
2881   SetMenuEnables(userThinkingEnables);
2882 }
2883
2884 void
2885 SetMachineThinkingEnables()
2886 {
2887   if (appData.noChessProgram) return;
2888   SetMenuEnables(machineThinkingEnables);
2889   switch (gameMode) {
2890   case MachinePlaysBlack:
2891   case MachinePlaysWhite:
2892   case TwoMachinesPlay:
2893 //    XtSetSensitive(XtNameToWidget(menuBarWidget,
2894 //                                ModeToWidgetName(gameMode)), True);
2895     break;
2896   default:
2897     break;
2898   }
2899 }
2900
2901 #define Abs(n) ((n)<0 ? -(n) : (n))
2902
2903 /*
2904  * Find a font that matches "pattern" that is as close as
2905  * possible to the targetPxlSize.  Prefer fonts that are k
2906  * pixels smaller to fonts that are k pixels larger.  The
2907  * pattern must be in the X Consortium standard format,
2908  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2909  * The return value should be freed with XtFree when no
2910  * longer needed.
2911  */
2912 char *FindFont(pattern, targetPxlSize)
2913      char *pattern;
2914      int targetPxlSize;
2915 {
2916     char **fonts, *p, *best, *scalable, *scalableTail;
2917     int i, j, nfonts, minerr, err, pxlSize;
2918
2919 #ifdef ENABLE_NLS
2920     char **missing_list;
2921     int missing_count;
2922     char *def_string, *base_fnt_lst, strInt[3];
2923     XFontSet fntSet;
2924     XFontStruct **fnt_list;
2925
2926     base_fnt_lst = calloc(1, strlen(pattern) + 3);
2927     sprintf(strInt, "%d", targetPxlSize);
2928     p = strstr(pattern, "--");
2929     strncpy(base_fnt_lst, pattern, p - pattern + 2);
2930     strcat(base_fnt_lst, strInt);
2931     strcat(base_fnt_lst, strchr(p + 2, '-'));
2932
2933     if ((fntSet = XCreateFontSet(xDisplay,
2934                                  base_fnt_lst,
2935                                  &missing_list,
2936                                  &missing_count,
2937                                  &def_string)) == NULL) {
2938
2939        fprintf(stderr, _("Unable to create font set.\n"));
2940        exit (2);
2941     }
2942
2943     nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
2944 #else
2945     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2946     if (nfonts < 1) {
2947         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2948                 programName, pattern);
2949         exit(2);
2950     }
2951 #endif
2952
2953     best = fonts[0];
2954     scalable = NULL;
2955     minerr = 999999;
2956     for (i=0; i<nfonts; i++) {
2957         j = 0;
2958         p = fonts[i];
2959         if (*p != '-') continue;
2960         while (j < 7) {
2961             if (*p == NULLCHAR) break;
2962             if (*p++ == '-') j++;
2963         }
2964         if (j < 7) continue;
2965         pxlSize = atoi(p);
2966         if (pxlSize == 0) {
2967             scalable = fonts[i];
2968             scalableTail = p;
2969         } else {
2970             err = pxlSize - targetPxlSize;
2971             if (Abs(err) < Abs(minerr) ||
2972                 (minerr > 0 && err < 0 && -err == minerr)) {
2973                 best = fonts[i];
2974                 minerr = err;
2975             }
2976         }
2977     }
2978     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2979         /* If the error is too big and there is a scalable font,
2980            use the scalable font. */
2981         int headlen = scalableTail - scalable;
2982         p = (char *) XtMalloc(strlen(scalable) + 10);
2983         while (isdigit(*scalableTail)) scalableTail++;
2984         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2985     } else {
2986         p = (char *) XtMalloc(strlen(best) + 1);
2987         strcpy(p, best);
2988     }
2989     if (appData.debugMode) {
2990         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
2991                 pattern, targetPxlSize, p);
2992     }
2993 #ifdef ENABLE_NLS
2994     if (missing_count > 0)
2995        XFreeStringList(missing_list);
2996     XFreeFontSet(xDisplay, fntSet);
2997 #else
2998      XFreeFontNames(fonts);
2999 #endif
3000     return p;
3001 }
3002
3003 void CreateGCs()
3004 {
3005   /* 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*/
3006
3007     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3008       | GCBackground | GCFunction | GCPlaneMask;
3009     XGCValues gc_values;
3010     GC copyInvertedGC;
3011
3012     gc_values.plane_mask = AllPlanes;
3013     gc_values.line_width = lineGap;
3014     gc_values.line_style = LineSolid;
3015     gc_values.function = GXcopy;
3016
3017     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3018     gc_values.background = XWhitePixel(xDisplay, xScreen);
3019     coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3020     XSetFont(xDisplay, coordGC, coordFontID);
3021
3022     if (appData.monoMode) {
3023         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3024         gc_values.background = XBlackPixel(xDisplay, xScreen);
3025         lightSquareGC = wbPieceGC
3026           = XtGetGC(shellWidget, value_mask, &gc_values);
3027
3028         gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3029         gc_values.background = XWhitePixel(xDisplay, xScreen);
3030         darkSquareGC = bwPieceGC
3031           = XtGetGC(shellWidget, value_mask, &gc_values);
3032
3033         if (DefaultDepth(xDisplay, xScreen) == 1) {
3034             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3035             gc_values.function = GXcopyInverted;
3036             copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3037             gc_values.function = GXcopy;
3038             if (XBlackPixel(xDisplay, xScreen) == 1) {
3039                 bwPieceGC = darkSquareGC;
3040                 wbPieceGC = copyInvertedGC;
3041             } else {
3042                 bwPieceGC = copyInvertedGC;
3043                 wbPieceGC = lightSquareGC;
3044             }
3045         }
3046     } else {
3047         gc_values.foreground = lightSquareColor;
3048         gc_values.background = darkSquareColor;
3049         lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3050
3051         gc_values.foreground = darkSquareColor;
3052         gc_values.background = lightSquareColor;
3053         darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3054
3055         gc_values.foreground = jailSquareColor;
3056         gc_values.background = jailSquareColor;
3057         jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3058
3059         gc_values.foreground = whitePieceColor;
3060         gc_values.background = darkSquareColor;
3061         wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3062
3063         gc_values.foreground = whitePieceColor;
3064         gc_values.background = lightSquareColor;
3065         wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3066
3067         gc_values.foreground = whitePieceColor;
3068         gc_values.background = jailSquareColor;
3069         wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3070
3071         gc_values.foreground = blackPieceColor;
3072         gc_values.background = darkSquareColor;
3073         bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3074
3075         gc_values.foreground = blackPieceColor;
3076         gc_values.background = lightSquareColor;
3077         blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3078
3079         gc_values.foreground = blackPieceColor;
3080         gc_values.background = jailSquareColor;
3081         bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3082     }
3083 }
3084
3085 void CreatePieces()
3086 {
3087   int i;
3088
3089   /* free if used 
3090   for(i=0;i<MAXPIECES;i++)
3091     {
3092       if(SVGpieces[i])
3093         {       
3094           g_free(SVGpieces[i]);
3095           SVGpieces[i]=NULL;
3096         }
3097     }
3098   */
3099
3100   /* reload these */
3101   SVGLightSquare   = load_pixbuf("svg/LightSquare.svg",squareSize);
3102   SVGDarkSquare    = load_pixbuf("svg/DarkSquare.svg",squareSize);
3103   SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
3104
3105
3106   /* get some defaults going */
3107   for(i=WhitePawn; i<DemotePiece+1; i++)
3108     SVGpieces[i]   = load_pixbuf("svg/NeutralSquare.svg",squareSize);
3109     
3110   SVGpieces[WhitePawn]   = load_pixbuf("svg/WhitePawn.svg",squareSize);
3111   SVGpieces[WhiteKnight] = load_pixbuf("svg/WhiteKnight.svg",squareSize);
3112   SVGpieces[WhiteBishop] = load_pixbuf("svg/WhiteBishop.svg",squareSize);
3113   SVGpieces[WhiteRook]   = load_pixbuf("svg/WhiteRook.svg",squareSize);
3114   SVGpieces[WhiteQueen]  = load_pixbuf("svg/WhiteQueen.svg",squareSize);
3115   SVGpieces[WhiteKing]   = load_pixbuf("svg/WhiteKing.svg",squareSize);
3116
3117   SVGpieces[BlackPawn]   = load_pixbuf("svg/BlackPawn.svg",squareSize);
3118   SVGpieces[BlackKnight] = load_pixbuf("svg/BlackKnight.svg",squareSize);
3119   SVGpieces[BlackBishop] = load_pixbuf("svg/BlackBishop.svg",squareSize);
3120   SVGpieces[BlackRook]   = load_pixbuf("svg/BlackRook.svg",squareSize);
3121   SVGpieces[BlackQueen]  = load_pixbuf("svg/BlackQueen.svg",squareSize);
3122   SVGpieces[BlackKing]   = load_pixbuf("svg/BlackKing.svg",squareSize);
3123
3124   return;
3125 }
3126
3127
3128 static void MenuBarSelect(w, addr, index)
3129      Widget w;
3130      caddr_t addr;
3131      caddr_t index;
3132 {
3133     XtActionProc proc = (XtActionProc) addr;
3134
3135     (proc)(NULL, NULL, NULL, NULL);
3136 }
3137
3138 void CreateMenuBarPopup(parent, name, mb)
3139      Widget parent;
3140      String name;
3141      Menu *mb;
3142 {
3143     int j;
3144     Widget menu, entry;
3145     MenuItem *mi;
3146     Arg args[16];
3147
3148     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3149                               parent, NULL, 0);
3150     j = 0;
3151     XtSetArg(args[j], XtNleftMargin, 20);   j++;
3152     XtSetArg(args[j], XtNrightMargin, 20);  j++;
3153     mi = mb->mi;
3154     while (mi->string != NULL) {
3155         if (strcmp(mi->string, "----") == 0) {
3156             entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3157                                           menu, args, j);
3158         } else {
3159           XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3160             entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
3161                                           menu, args, j+1);
3162             XtAddCallback(entry, XtNcallback,
3163                           (XtCallbackProc) MenuBarSelect,
3164                           (caddr_t) mi->proc);
3165         }
3166         mi++;
3167     }
3168 }
3169
3170 Widget CreateMenuBar(mb)
3171      Menu *mb;
3172 {
3173     int j;
3174     Widget anchor, menuBar;
3175     Arg args[16];
3176     char menuName[MSG_SIZ];
3177
3178     j = 0;
3179     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
3180     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3181     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3182     menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3183                              formWidget, args, j);
3184
3185     while (mb->name != NULL) {
3186         strcpy(menuName, "menu");
3187         strcat(menuName, mb->name);
3188         j = 0;
3189         XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
3190         if (tinyLayout) {
3191             char shortName[2];
3192             shortName[0] = _(mb->name)[0];
3193             shortName[1] = NULLCHAR;
3194             XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3195         }
3196       else {
3197           XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3198       }
3199
3200         XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3201         anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3202                                        menuBar, args, j);
3203         CreateMenuBarPopup(menuBar, menuName, mb);
3204         mb++;
3205     }
3206     return menuBar;
3207 }
3208
3209
3210 Widget
3211 CreatePieceMenu(name, color)
3212      char *name;
3213      int color;
3214 {
3215     int i;
3216     Widget entry, menu;
3217     Arg args[16];
3218     ChessSquare selection;
3219
3220     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3221                               boardWidget, args, 0);
3222
3223     for (i = 0; i < PIECE_MENU_SIZE; i++) {
3224         String item = pieceMenuStrings[color][i];
3225
3226         if (strcmp(item, "----") == 0) {
3227             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3228                                           menu, NULL, 0);
3229         } else {
3230           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3231             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3232                                 menu, args, 1);
3233             selection = pieceMenuTranslation[color][i];
3234             XtAddCallback(entry, XtNcallback,
3235                           (XtCallbackProc) PieceMenuSelect,
3236                           (caddr_t) selection);
3237             if (selection == WhitePawn || selection == BlackPawn) {
3238                 XtSetArg(args[0], XtNpopupOnEntry, entry);
3239                 XtSetValues(menu, args, 1);
3240             }
3241         }
3242     }
3243     return menu;
3244 }
3245
3246 void
3247 CreatePieceMenus()
3248 {
3249     int i;
3250     Widget entry;
3251     Arg args[16];
3252     ChessSquare selection;
3253
3254 //    whitePieceMenu = CreatePieceMenu("menuW", 0);
3255 //    blackPieceMenu = CreatePieceMenu("menuB", 1);
3256 //
3257 //    XtRegisterGrabAction(PieceMenuPopup, True,
3258 //                       (unsigned)(ButtonPressMask|ButtonReleaseMask),
3259 //                       GrabModeAsync, GrabModeAsync);
3260 //
3261 //    XtSetArg(args[0], XtNlabel, _("Drop"));
3262 //    dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3263 //                                boardWidget, args, 1);
3264 //    for (i = 0; i < DROP_MENU_SIZE; i++) {
3265 //      String item = dropMenuStrings[i];
3266 //
3267 //      if (strcmp(item, "----") == 0) {
3268 //          entry = XtCreateManagedWidget(item, smeLineObjectClass,
3269 //                                        dropMenu, NULL, 0);
3270 //      } else {
3271 //          XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3272 //          entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3273 //                                dropMenu, args, 1);
3274 //          selection = dropMenuTranslation[i];
3275 //          XtAddCallback(entry, XtNcallback,
3276 //                        (XtCallbackProc) DropMenuSelect,
3277 //                        (caddr_t) selection);
3278 //      }
3279 //    }
3280 }
3281
3282 void SetupDropMenu()
3283 {
3284     int i, j, count;
3285     char label[32];
3286     Arg args[16];
3287     Widget entry;
3288     char* p;
3289
3290     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3291         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3292         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3293                    dmEnables[i].piece);
3294         XtSetSensitive(entry, p != NULL || !appData.testLegality
3295                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3296                                        && !appData.icsActive));
3297         count = 0;
3298         while (p && *p++ == dmEnables[i].piece) count++;
3299         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
3300         j = 0;
3301         XtSetArg(args[j], XtNlabel, label); j++;
3302         XtSetValues(entry, args, j);
3303     }
3304 }
3305
3306 void PieceMenuPopup(w, event, params, num_params)
3307      Widget w;
3308      XEvent *event;
3309      String *params;
3310      Cardinal *num_params;
3311 {
3312     String whichMenu;
3313     if (event->type != ButtonPress) return;
3314     if (errorUp) ErrorPopDown();
3315     switch (gameMode) {
3316       case EditPosition:
3317       case IcsExamining:
3318         whichMenu = params[0];
3319         break;
3320       case IcsPlayingWhite:
3321       case IcsPlayingBlack:
3322       case EditGame:
3323       case MachinePlaysWhite:
3324       case MachinePlaysBlack:
3325         if (appData.testLegality &&
3326             gameInfo.variant != VariantBughouse &&
3327             gameInfo.variant != VariantCrazyhouse) return;
3328         SetupDropMenu();
3329         whichMenu = "menuD";
3330         break;
3331       default:
3332         return;
3333     }
3334
3335     if (((pmFromX = EventToSquare(event->xbutton.x, BOARD_WIDTH)) < 0) ||
3336         ((pmFromY = EventToSquare(event->xbutton.y, BOARD_HEIGHT)) < 0)) {
3337         pmFromX = pmFromY = -1;
3338         return;
3339     }
3340     if (flipView)
3341       pmFromX = BOARD_WIDTH - 1 - pmFromX;
3342     else
3343       pmFromY = BOARD_HEIGHT - 1 - pmFromY;
3344
3345     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3346 }
3347
3348 static void PieceMenuSelect(w, piece, junk)
3349      Widget w;
3350      ChessSquare piece;
3351      caddr_t junk;
3352 {
3353     if (pmFromX < 0 || pmFromY < 0) return;
3354     EditPositionMenuEvent(piece, pmFromX, pmFromY);
3355 }
3356
3357 static void DropMenuSelect(w, piece, junk)
3358      Widget w;
3359      ChessSquare piece;
3360      caddr_t junk;
3361 {
3362     if (pmFromX < 0 || pmFromY < 0) return;
3363     DropMenuEvent(piece, pmFromX, pmFromY);
3364 }
3365
3366 /*
3367  * If the user selects on a border boundary, return -1; if off the board,
3368  *   return -2.  Otherwise map the event coordinate to the square.
3369  */
3370 int EventToSquare(x, limit)
3371      int x;
3372 {
3373     if (x <= 0)
3374       return -2;
3375     if (x < lineGap)
3376       return -1;
3377     x -= lineGap;
3378     if ((x % (squareSize + lineGap)) >= squareSize)
3379       return -1;
3380     x /= (squareSize + lineGap);
3381     if (x >= limit)
3382       return -2;
3383     return x;
3384 }
3385
3386 static void do_flash_delay(msec)
3387      unsigned long msec;
3388 {
3389     TimeDelay(msec);
3390 }
3391
3392 static void drawHighlight(file, rank, line_type)
3393      int file, rank, line_type;
3394 {
3395     int x, y;
3396     cairo_t *cr;
3397
3398     if (lineGap == 0 || appData.blindfold) return;
3399
3400     if (flipView)
3401       {
3402         x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
3403           (squareSize + lineGap);
3404         y = lineGap/2 + rank * (squareSize + lineGap);
3405       }
3406     else
3407       {
3408         x = lineGap/2 + file * (squareSize + lineGap);
3409         y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
3410           (squareSize + lineGap);
3411       }
3412
3413     /* get a cairo_t */
3414     cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3415
3416     /* draw the highlight */
3417     cairo_move_to (cr, x, y);
3418     cairo_rel_line_to (cr, 0,squareSize+lineGap);
3419     cairo_rel_line_to (cr, squareSize+lineGap,0);
3420     cairo_rel_line_to (cr, 0,-squareSize-lineGap);
3421     cairo_close_path (cr);
3422
3423     cairo_set_line_width (cr, lineGap);
3424     switch(line_type)
3425       {
3426         /* TODO: use appdata colors */
3427       case LINE_TYPE_HIGHLIGHT:
3428         cairo_set_source_rgba (cr, 1, 1, 0, 1.0);
3429         break;
3430       case LINE_TYPE_PRE:
3431         cairo_set_source_rgba (cr, 1, 0, 0, 1.0);
3432         break;
3433       case LINE_TYPE_NORMAL:
3434       default:
3435         cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
3436       }
3437
3438     cairo_stroke (cr);
3439
3440     /* free memory */
3441     cairo_destroy (cr);
3442
3443     return;
3444 }
3445
3446 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
3447 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
3448
3449 void
3450 SetHighlights(fromX, fromY, toX, toY)
3451      int fromX, fromY, toX, toY;
3452 {
3453     if (hi1X != fromX || hi1Y != fromY)
3454       {
3455         if (hi1X >= 0 && hi1Y >= 0)
3456           {
3457             drawHighlight(hi1X, hi1Y, LINE_TYPE_NORMAL);
3458           }
3459         if (fromX >= 0 && fromY >= 0)
3460           {
3461             drawHighlight(fromX, fromY, LINE_TYPE_HIGHLIGHT);
3462           }
3463       }
3464     if (hi2X != toX || hi2Y != toY)
3465       {
3466         if (hi2X >= 0 && hi2Y >= 0)
3467           {
3468             drawHighlight(hi2X, hi2Y, LINE_TYPE_NORMAL);
3469           }
3470         if (toX >= 0 && toY >= 0)
3471           {
3472             drawHighlight(toX, toY, LINE_TYPE_HIGHLIGHT);
3473           }
3474       }
3475     hi1X = fromX;
3476     hi1Y = fromY;
3477     hi2X = toX;
3478     hi2Y = toY;
3479
3480     return;
3481 }
3482
3483 void
3484 ClearHighlights()
3485 {
3486     SetHighlights(-1, -1, -1, -1);
3487 }
3488
3489
3490 void
3491 SetPremoveHighlights(fromX, fromY, toX, toY)
3492      int fromX, fromY, toX, toY;
3493 {
3494     if (pm1X != fromX || pm1Y != fromY)
3495       {
3496         if (pm1X >= 0 && pm1Y >= 0)
3497           {
3498             drawHighlight(pm1X, pm1Y, LINE_TYPE_NORMAL);
3499           }
3500         if (fromX >= 0 && fromY >= 0)
3501           {
3502             drawHighlight(fromX, fromY, LINE_TYPE_PRE);
3503           }
3504       }
3505     if (pm2X != toX || pm2Y != toY)
3506       {
3507         if (pm2X >= 0 && pm2Y >= 0)
3508           {
3509             drawHighlight(pm2X, pm2Y, LINE_TYPE_NORMAL);
3510           }
3511         if (toX >= 0 && toY >= 0)
3512           {
3513             drawHighlight(toX, toY, LINE_TYPE_PRE);
3514           }
3515       }
3516
3517     pm1X = fromX;
3518     pm1Y = fromY;
3519     pm2X = toX;
3520     pm2Y = toY;
3521
3522     return;
3523 }
3524
3525 void
3526 ClearPremoveHighlights()
3527 {
3528   SetPremoveHighlights(-1, -1, -1, -1);
3529 }
3530
3531 static void BlankSquare(x, y, color, piece, dest)
3532      int x, y, color;
3533      ChessSquare piece;
3534      Drawable dest;
3535 {
3536       GdkPixbuf *pb;
3537
3538       switch (color) 
3539         {
3540         case 0: /* dark */
3541           pb = SVGDarkSquare;
3542           break;
3543         case 1: /* light */
3544           pb = SVGLightSquare;
3545           break;
3546         case 2: /* neutral */
3547         default:
3548           pb = SVGNeutralSquare;
3549           break;
3550         }
3551       gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,pb,0,0,x,y,-1,-1, GDK_RGB_DITHER_NORMAL, 0, 0);
3552       return;
3553 }
3554
3555 static void DrawPiece(piece, square_color, x, y, dest)
3556      ChessSquare piece;
3557      int square_color, x, y;
3558      Drawable dest;
3559 {
3560   /* redraw background, since piece might be transparent in some areas */
3561   BlankSquare(x,y,square_color,piece,dest);
3562
3563   /* draw piece */
3564   gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,
3565                   GDK_PIXBUF(SVGpieces[piece]),0,0,x,y,-1,-1,
3566                   GDK_RGB_DITHER_NORMAL, 0, 0);
3567   return ;
3568 }
3569
3570 /* [HR] determine square color depending on chess variant. */
3571 static int SquareColor(row, column)
3572      int row, column;
3573 {
3574     int square_color;
3575
3576     if (gameInfo.variant == VariantXiangqi) {
3577         if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
3578             square_color = 1;
3579         } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
3580             square_color = 0;
3581         } else if (row <= 4) {
3582             square_color = 0;
3583         } else {
3584             square_color = 1;
3585         }
3586     } else {
3587         square_color = ((column + row) % 2) == 1;
3588     }
3589
3590     /* [hgm] holdings: next line makes all holdings squares light */
3591     if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
3592
3593     return square_color;
3594 }
3595
3596 void DrawSquare(row, column, piece, do_flash)
3597      int row, column, do_flash;
3598      ChessSquare piece;
3599 {
3600     int square_color, x, y;
3601     int i;
3602     char string[2];
3603     int flash_delay;
3604
3605     /* Calculate delay in milliseconds (2-delays per complete flash) */
3606     flash_delay = 500 / appData.flashRate;
3607
3608     /* calculate x and y coordinates from row and column */
3609     if (flipView)
3610       {
3611         x = lineGap + ((BOARD_WIDTH-1)-column) *
3612           (squareSize + lineGap);
3613         y = lineGap + row * (squareSize + lineGap);
3614       }
3615     else
3616       {
3617         x = lineGap + column * (squareSize + lineGap);
3618         y = lineGap + ((BOARD_HEIGHT-1)-row) *
3619           (squareSize + lineGap);
3620       }
3621
3622     square_color = SquareColor(row, column);
3623
3624     // [HGM] holdings: blank out area between board and holdings
3625     if ( column == BOARD_LEFT-1 ||  column == BOARD_RGHT
3626          || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
3627          || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) )
3628       {
3629         BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
3630
3631         // [HGM] print piece counts next to holdings
3632         string[1] = NULLCHAR;
3633         if(piece > 1)
3634           {
3635             cairo_text_extents_t extents;
3636             cairo_t *cr;
3637             int  xpos, ypos;
3638
3639             /* get a cairo_t */
3640             cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3641
3642             string[0] = '0' + piece;
3643
3644             /* TODO this has to go into the font-selection */
3645             cairo_select_font_face (cr, "Sans",
3646                                     CAIRO_FONT_SLANT_NORMAL,
3647                                     CAIRO_FONT_WEIGHT_NORMAL);
3648
3649             cairo_set_font_size (cr, 12.0);
3650             cairo_text_extents (cr, string, &extents);
3651
3652             if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) )
3653               {
3654                 xpos= x + squareSize - extents.width - 2;
3655                 ypos= y + extents.y_bearing + 1;
3656               }
3657             if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1)
3658               {
3659                 xpos= x + 2;
3660                 ypos = y + extents.y_bearing + 1;
3661               }
3662
3663             /* TODO mono mode? */
3664             cairo_move_to (cr, xpos, ypos);
3665             cairo_text_path (cr, string);
3666             cairo_set_source_rgb (cr, 1.0, 1.0, 1);
3667             cairo_fill_preserve (cr);
3668             cairo_set_source_rgb (cr, 0, 0, 0);
3669             cairo_set_line_width (cr, 0.1);
3670             cairo_stroke (cr);
3671
3672             /* free memory */
3673             cairo_destroy (cr);
3674           }
3675       }
3676     else
3677       {
3678         /* square on the board */
3679         if (piece == EmptySquare || appData.blindfold)
3680           {
3681             BlankSquare(x, y, square_color, piece, xBoardWindow);
3682           }
3683         else
3684           {
3685             if (do_flash && appData.flashCount > 0)
3686               {
3687                 for (i=0; i<appData.flashCount; ++i)
3688                   {
3689
3690                     DrawPiece(piece, square_color, x, y, xBoardWindow);
3691                     do_flash_delay(flash_delay);
3692
3693                     BlankSquare(x, y, square_color, piece, xBoardWindow);
3694                     do_flash_delay(flash_delay);
3695                   }
3696               }
3697             DrawPiece(piece, square_color, x, y, xBoardWindow);
3698           }
3699       }
3700
3701     /* show coordinates if necessary */
3702     if(appData.showCoords)
3703       {
3704         cairo_text_extents_t extents;
3705         cairo_t *cr;
3706         int  xpos, ypos;
3707
3708         /* TODO this has to go into the font-selection */
3709         cairo_select_font_face (cr, "Sans",
3710                                 CAIRO_FONT_SLANT_NORMAL,
3711                                 CAIRO_FONT_WEIGHT_NORMAL);
3712         cairo_set_font_size (cr, 12.0);
3713
3714         string[1] = NULLCHAR;
3715
3716         /* get a cairo_t */
3717         cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3718
3719         if (row == (flipView ? BOARD_HEIGHT-1 : 0) &&
3720             column >= BOARD_LEFT && column < BOARD_RGHT)
3721           {
3722             string[0] = 'a' + column - BOARD_LEFT;
3723             cairo_text_extents (cr, string, &extents);
3724
3725             xpos = x + squareSize - extents.width - 2;
3726             ypos = y + squareSize - extents.height - extents.y_bearing - 1;
3727
3728             if (appData.monoMode)
3729               { /*TODO*/
3730               }
3731             else
3732               {
3733               }
3734
3735             cairo_move_to (cr, xpos, ypos);
3736             cairo_text_path (cr, string);
3737             cairo_set_source_rgb (cr, 0.0, 0.0, 0);
3738             cairo_fill_preserve (cr);
3739             cairo_set_source_rgb (cr, 0, 1.0, 0);
3740             cairo_set_line_width (cr, 0.1);
3741             cairo_stroke (cr);
3742           }
3743         if ( column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT))
3744           {
3745
3746             string[0] = ONE + row;
3747             cairo_text_extents (cr, string, &extents);
3748
3749             xpos = x + 2;
3750             ypos = y + extents.height + 1;
3751
3752             if (appData.monoMode)
3753               { /*TODO*/
3754               }
3755             else
3756               {
3757               }
3758
3759             cairo_move_to (cr, xpos, ypos);
3760             cairo_text_path (cr, string);
3761             cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
3762             cairo_fill_preserve (cr);
3763             cairo_set_source_rgb (cr, 0, 0, 1.0);
3764             cairo_set_line_width (cr, 0.1);
3765             cairo_stroke (cr);
3766
3767           }
3768         /* free memory */
3769         cairo_destroy (cr);
3770       }
3771
3772     return;
3773 }
3774
3775
3776 /* Returns 1 if there are "too many" differences between b1 and b2
3777    (i.e. more than 1 move was made) */
3778 static int too_many_diffs(b1, b2)
3779      Board b1, b2;
3780 {
3781     int i, j;
3782     int c = 0;
3783
3784     for (i=0; i<BOARD_HEIGHT; ++i) {
3785         for (j=0; j<BOARD_WIDTH; ++j) {
3786             if (b1[i][j] != b2[i][j]) {
3787                 if (++c > 4)    /* Castling causes 4 diffs */
3788                   return 1;
3789             }
3790         }
3791     }
3792
3793     return 0;
3794 }
3795
3796 /* Matrix describing castling maneuvers */
3797 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
3798 static int castling_matrix[4][5] = {
3799     { 0, 0, 4, 3, 2 },          /* 0-0-0, white */
3800     { 0, 7, 4, 5, 6 },          /* 0-0,   white */
3801     { 7, 0, 4, 3, 2 },          /* 0-0-0, black */
3802     { 7, 7, 4, 5, 6 }           /* 0-0,   black */
3803 };
3804
3805 /* Checks whether castling occurred. If it did, *rrow and *rcol
3806    are set to the destination (row,col) of the rook that moved.
3807
3808    Returns 1 if castling occurred, 0 if not.
3809
3810    Note: Only handles a max of 1 castling move, so be sure
3811    to call too_many_diffs() first.
3812    */
3813 static int check_castle_draw(newb, oldb, rrow, rcol)
3814      Board newb, oldb;
3815      int *rrow, *rcol;
3816 {
3817     int i, *r, j;
3818     int match;
3819
3820     /* For each type of castling... */
3821     for (i=0; i<4; ++i) {
3822         r = castling_matrix[i];
3823
3824         /* Check the 4 squares involved in the castling move */
3825         match = 0;
3826         for (j=1; j<=4; ++j) {
3827             if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
3828                 match = 1;
3829                 break;
3830             }
3831         }
3832
3833         if (!match) {
3834             /* All 4 changed, so it must be a castling move */
3835             *rrow = r[0];
3836             *rcol = r[3];
3837             return 1;
3838         }
3839     }
3840     return 0;
3841 }
3842
3843 static int damage[BOARD_SIZE][BOARD_SIZE];
3844
3845 /*
3846  * event handler for redrawing the board
3847  */
3848 void DrawPosition( repaint, board)
3849      /*Boolean*/int repaint;
3850                 Board board;
3851 {
3852   int i, j, do_flash;
3853   static int lastFlipView = 0;
3854   static int lastBoardValid = 0;
3855   static Board lastBoard;
3856   int rrow, rcol;
3857
3858   if (board == NULL) {
3859     if (!lastBoardValid) return;
3860     board = lastBoard;
3861   }
3862   if (!lastBoardValid || lastFlipView != flipView) {
3863     //    XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
3864     // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
3865     //  args, 1);
3866   }
3867
3868   /*
3869    * It would be simpler to clear the window with XClearWindow()
3870    * but this causes a very distracting flicker.
3871    */
3872
3873   if (!repaint && lastBoardValid && lastFlipView == flipView)
3874     {
3875       /* If too much changes (begin observing new game, etc.), don't
3876          do flashing */
3877       do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
3878
3879       /* Special check for castling so we don't flash both the king
3880          and the rook (just flash the king). */
3881       if (do_flash)
3882         {
3883           if (check_castle_draw(board, lastBoard, &rrow, &rcol))
3884             {
3885               /* Draw rook with NO flashing. King will be drawn flashing later */
3886               DrawSquare(rrow, rcol, board[rrow][rcol], 0);
3887               lastBoard[rrow][rcol] = board[rrow][rcol];
3888             }
3889         }
3890
3891       /* First pass -- Draw (newly) empty squares and repair damage.
3892          This prevents you from having a piece show up twice while it
3893          is flashing on its new square */
3894       for (i = 0; i < BOARD_HEIGHT; i++)
3895         for (j = 0; j < BOARD_WIDTH; j++)
3896           if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
3897               || damage[i][j])
3898             {
3899               DrawSquare(i, j, board[i][j], 0);
3900               damage[i][j] = False;
3901             }
3902
3903       /* Second pass -- Draw piece(s) in new position and flash them */
3904       for (i = 0; i < BOARD_HEIGHT; i++)
3905         for (j = 0; j < BOARD_WIDTH; j++)
3906           if (board[i][j] != lastBoard[i][j])
3907             {
3908               DrawSquare(i, j, board[i][j], do_flash);
3909             }
3910     }
3911   else
3912     {
3913       /* redraw Grid */
3914       if (lineGap > 0)
3915         {
3916           int x1,x2,y1,y2;
3917           cairo_t *cr;
3918
3919           /* get a cairo_t */
3920           cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3921
3922           cairo_set_line_width (cr, lineGap);
3923
3924           /* TODO: use appdata colors */
3925           cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
3926
3927           cairo_stroke (cr);
3928
3929           for (i = 0; i < BOARD_HEIGHT + 1; i++)
3930             {
3931               x1 = 0;
3932               x2 = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3933               y1 = y2 = lineGap / 2 + (i * (squareSize + lineGap));
3934
3935               cairo_move_to (cr, x1, y1);
3936               cairo_rel_line_to (cr, x2,0);
3937               cairo_stroke (cr);
3938             }
3939
3940           for (j = 0; j < BOARD_WIDTH + 1; j++)
3941             {
3942               y1 = 0;
3943               y2 = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3944               x1 = x2  = lineGap / 2 + (j * (squareSize + lineGap));
3945
3946               cairo_move_to (cr, x1, y1);
3947               cairo_rel_line_to (cr, 0, y2);
3948               cairo_stroke (cr);
3949             }
3950
3951           /* free memory */
3952           cairo_destroy (cr);
3953         }
3954
3955       /* draw pieces */
3956       for (i = 0; i < BOARD_HEIGHT; i++)
3957         for (j = 0; j < BOARD_WIDTH; j++)
3958           {
3959             DrawSquare(i, j, board[i][j], 0);
3960             damage[i][j] = False;
3961           }
3962     }
3963
3964   CopyBoard(lastBoard, board);
3965   lastBoardValid = 1;
3966   lastFlipView = flipView;
3967
3968   /* Draw highlights */
3969   if (pm1X >= 0 && pm1Y >= 0)
3970     {
3971       drawHighlight(pm1X, pm1Y, LINE_TYPE_PRE);
3972     }
3973   if (pm2X >= 0 && pm2Y >= 0)
3974     {
3975       drawHighlight(pm2X, pm2Y, LINE_TYPE_PRE);
3976     }
3977   if (hi1X >= 0 && hi1Y >= 0)
3978     {
3979       drawHighlight(hi1X, hi1Y, LINE_TYPE_HIGHLIGHT);
3980     }
3981   if (hi2X >= 0 && hi2Y >= 0)
3982     {
3983       drawHighlight(hi2X, hi2Y, LINE_TYPE_HIGHLIGHT);
3984     }
3985
3986   /* If piece being dragged around board, must redraw that too */
3987   DrawDragPiece();
3988
3989   return;
3990 }
3991
3992 /*
3993  * event handler for parsing user moves
3994  */
3995 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3996 //       way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3997 //       it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3998 //       should be made to use the new way, of calling UserMoveTest early  to determine the legality of the
3999 //       move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4000 //       and at the end FinishMove() to perform the move after optional promotion popups.
4001 //       For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4002 void HandleUserMove(w, event, prms, nprms)
4003      Widget w;
4004      XEvent *event;
4005      String *prms;
4006      Cardinal *nprms;
4007 {
4008     int x, y;
4009     Boolean saveAnimate;
4010     static int second = 0, promotionChoice = 0;
4011     ChessMove moveType;
4012
4013     if (w != boardWidget || errorExitStatus != -1) return;
4014
4015     x = EventToSquare(event->xbutton.x, BOARD_WIDTH);
4016     y = EventToSquare(event->xbutton.y, BOARD_HEIGHT);
4017     if (!flipView && y >= 0) {
4018         y = BOARD_HEIGHT - 1 - y;
4019     }
4020     if (flipView && x >= 0) {
4021         x = BOARD_WIDTH - 1 - x;
4022     }
4023
4024     if(promotionChoice) { // we are waiting for a click to indicate promotion piece
4025         if(event->type == ButtonRelease) return; // ignore upclick of click-click destination
4026         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel
4027         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);
4028         if(gameInfo.holdingsWidth && 
4029                 (WhiteOnMove(currentMove) 
4030                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0
4031                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {
4032             // click in right holdings, for determining promotion piece
4033             ChessSquare p = boards[currentMove][y][x];
4034             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);
4035             if(p != EmptySquare) {
4036                 FinishMove(NormalMove, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));
4037                 fromX = fromY = -1;
4038                 return;
4039             }
4040         }
4041         DrawPosition(FALSE, boards[currentMove]);
4042         return;
4043     }
4044     if (event->type == ButtonPress) ErrorPopDown();
4045
4046     if (promotionUp) {
4047         if (event->type == ButtonPress) {
4048 //          XtPopdown(promotionShell);
4049 //          XtDestroyWidget(promotionShell);
4050             promotionUp = False;
4051             ClearHighlights();
4052             fromX = fromY = -1;
4053         } else {
4054             return;
4055         }
4056     }
4057
4058     /* [HGM] holdings: next 5 lines: ignore all clicks between board and holdings */
4059     if(event->type == ButtonPress
4060             && ( x == BOARD_LEFT-1 || 
4061                  x == BOARD_RGHT   || 
4062                  (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize ) || 
4063                  (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)) )
4064         return;
4065
4066     if (fromX == -1) {
4067         if (event->type == ButtonPress) {
4068             /* First square, prepare to drag */
4069             if (OKToStartUserMove(x, y)) {
4070                 fromX = x;
4071                 fromY = y;
4072                 second = 0;
4073                 DragPieceBegin(event->xbutton.x, event->xbutton.y);
4074                 if (appData.highlightDragging) {
4075                     SetHighlights(x, y, -1, -1);
4076                 }
4077             }
4078         }
4079         return;
4080     }
4081
4082     /* fromX != -1 */
4083     if (event->type == ButtonRelease && x == fromX && y == fromY) {
4084     /* Click on single square in stead of drag-drop */
4085         DragPieceEnd(event->xbutton.x, event->xbutton.y);
4086         if (appData.animateDragging) {
4087             /* Undo animation damage if any */
4088             DrawPosition(FALSE, NULL);
4089         }
4090         if (second) {
4091             /* Second up/down in same square; just abort move */
4092             second = 0;
4093             fromX = fromY = -1;
4094             ClearHighlights();
4095             gotPremove = 0;
4096             ClearPremoveHighlights();
4097         } else {
4098             /* First upclick in same square; start click-click mode */
4099             SetHighlights(x, y, -1, -1);
4100         }
4101         return;
4102     }
4103
4104     moveType = UserMoveTest(fromX, fromY, x, y, NULLCHAR, event->type == ButtonRelease);
4105
4106     if (moveType == Comment) { // kludge for indicating capture-own on Press
4107       /* Clicked again on same color piece -- changed his mind */
4108       /* note that re-clicking same square always hits same color piece */
4109       second = (x == fromX && y == fromY);
4110       if (appData.highlightDragging) {
4111         SetHighlights(x, y, -1, -1);
4112       } else {
4113         ClearHighlights();
4114       }
4115       if (OKToStartUserMove(x, y)) {
4116         fromX = x;
4117         fromY = y;
4118         DragPieceBegin(event->xbutton.x, event->xbutton.y);
4119       }
4120       return;
4121     }
4122
4123     if(moveType == AmbiguousMove) { // kludge to indicate edit-position move
4124       fromX = fromY = -1; 
4125       ClearHighlights();
4126       DragPieceEnd(event->xbutton.x, event->xbutton.y);
4127       DrawPosition(FALSE, boards[currentMove]);
4128       return;
4129     }
4130
4131     /* Complete move; (x,y) is now different from (fromX, fromY) on both Press and Release */
4132     toX = x;
4133     toY = y;
4134     saveAnimate = appData.animate;
4135     if (event->type == ButtonPress) {
4136         /* Finish clickclick move */
4137         if (appData.animate || appData.highlightLastMove) {
4138             SetHighlights(fromX, fromY, toX, toY);
4139         } else {
4140             ClearHighlights();
4141         }
4142     } else {
4143         /* Finish drag move */
4144         if (appData.highlightLastMove) {
4145             SetHighlights(fromX, fromY, toX, toY);
4146         } else {
4147             ClearHighlights();
4148         }
4149         DragPieceEnd(event->xbutton.x, event->xbutton.y);
4150         /* Don't animate move and drag both */
4151         appData.animate = FALSE;
4152     }
4153     if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||
4154         (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&
4155             appData.alwaysPromoteToQueen) { // promotion, but no choice
4156       FinishMove(moveType, fromX, fromY, toX, toY, 'q');
4157     } else
4158     if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {
4159         SetHighlights(fromX, fromY, toX, toY);
4160         if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {
4161             // [HGM] super: promotion to captured piece selected from holdings
4162             ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];
4163             promotionChoice = TRUE;
4164             // kludge follows to temporarily execute move on display, without promoting yet
4165             boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank
4166             boards[currentMove][toY][toX] = p;
4167             DrawPosition(FALSE, boards[currentMove]);
4168             boards[currentMove][fromY][fromX] = p; // take back, but display stays
4169             boards[currentMove][toY][toX] = q;
4170             DisplayMessage("Click in holdings to choose piece", "");
4171             return;
4172         }
4173         PromotionPopUp();
4174         goto skipClearingFrom; // the skipped stuff is done asynchronously by PromotionCallback
4175     } else
4176     if(moveType != ImpossibleMove) { // valid move, but no promotion
4177       FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);
4178     } else { // invalid move; could have set premove
4179       ClearHighlights();
4180     }
4181             if (!appData.highlightLastMove || gotPremove) ClearHighlights();
4182             if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
4183             fromX = fromY = -1;
4184 skipClearingFrom:
4185     appData.animate = saveAnimate;
4186     if (appData.animate || appData.animateDragging) {
4187         /* Undo animation damage if needed */
4188         DrawPosition(FALSE, NULL);
4189     }
4190 }
4191
4192 void AnimateUserMove (Widget w, XEvent * event,
4193                       String * params, Cardinal * nParams)
4194 {
4195     DragPieceMove(event->xmotion.x, event->xmotion.y);
4196 }
4197
4198 Widget CommentCreate(name, text, mutable, callback, lines)
4199      char *name, *text;
4200      int /*Boolean*/ mutable;
4201      XtCallbackProc callback;
4202      int lines;
4203 {
4204     Arg args[16];
4205     Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
4206     Dimension bw_width;
4207     int j;
4208
4209     j = 0;
4210     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
4211     XtGetValues(boardWidget, args, j);
4212
4213     j = 0;
4214     XtSetArg(args[j], XtNresizable, True);  j++;
4215 #if TOPLEVEL
4216     shell =
4217       XtCreatePopupShell(name, topLevelShellWidgetClass,
4218                          shellWidget, args, j);
4219 #else
4220     shell =
4221       XtCreatePopupShell(name, transientShellWidgetClass,
4222                          shellWidget, args, j);
4223 #endif
4224     layout =
4225       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4226                             layoutArgs, XtNumber(layoutArgs));
4227     form =
4228       XtCreateManagedWidget("form", formWidgetClass, layout,
4229                             formArgs, XtNumber(formArgs));
4230
4231     j = 0;
4232     if (mutable) {
4233         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
4234         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
4235     }
4236     XtSetArg(args[j], XtNstring, text);  j++;
4237     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
4238     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
4239     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
4240     XtSetArg(args[j], XtNright, XtChainRight);  j++;
4241     XtSetArg(args[j], XtNresizable, True);  j++;
4242     XtSetArg(args[j], XtNwidth, bw_width);  j++; /*force wider than buttons*/
4243     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4244     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
4245     XtSetArg(args[j], XtNautoFill, True);  j++;
4246     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4247     edit =
4248       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4249
4250     if (mutable) {
4251         j = 0;
4252         XtSetArg(args[j], XtNfromVert, edit);  j++;
4253         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4254         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4255         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4256         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4257         b_ok =
4258           XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
4259         XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
4260
4261         j = 0;
4262         XtSetArg(args[j], XtNfromVert, edit);  j++;
4263         XtSetArg(args[j], XtNfromHoriz, b_ok);  j++;
4264         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4265         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4266         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4267         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4268         b_cancel =
4269           XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
4270         XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
4271
4272         j = 0;
4273         XtSetArg(args[j], XtNfromVert, edit);  j++;
4274         XtSetArg(args[j], XtNfromHoriz, b_cancel);  j++;
4275         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4276         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4277         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4278         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4279         b_clear =
4280           XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
4281         XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
4282     } else {
4283         j = 0;
4284         XtSetArg(args[j], XtNfromVert, edit);  j++;
4285         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4286         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4287         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4288         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4289         b_close =
4290           XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
4291         XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
4292
4293         j = 0;
4294         XtSetArg(args[j], XtNfromVert, edit);  j++;
4295         XtSetArg(args[j], XtNfromHoriz, b_close);  j++;
4296         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4297         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4298         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4299         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4300         b_edit =
4301           XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
4302         XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
4303     }
4304
4305     XtRealizeWidget(shell);
4306
4307     if (commentX == -1) {
4308         int xx, yy;
4309         Window junk;
4310         Dimension pw_height;
4311         Dimension ew_height;
4312
4313         j = 0;
4314         XtSetArg(args[j], XtNheight, &ew_height);  j++;
4315         XtGetValues(edit, args, j);
4316
4317         j = 0;
4318         XtSetArg(args[j], XtNheight, &pw_height);  j++;
4319         XtGetValues(shell, args, j);
4320         commentH = pw_height + (lines - 1) * ew_height;
4321         commentW = bw_width - 16;
4322
4323         XSync(xDisplay, False);
4324 #ifdef NOTDEF
4325         /* This code seems to tickle an X bug if it is executed too soon
4326            after xboard starts up.  The coordinates get transformed as if
4327            the main window was positioned at (0, 0).
4328            */
4329         XtTranslateCoords(shellWidget,
4330                           (bw_width - commentW) / 2, 0 - commentH / 2,
4331                           &commentX, &commentY);
4332 #else  /*!NOTDEF*/
4333         XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4334                               RootWindowOfScreen(XtScreen(shellWidget)),
4335                               (bw_width - commentW) / 2, 0 - commentH / 2,
4336                               &xx, &yy, &junk);
4337         commentX = xx;
4338         commentY = yy;
4339 #endif /*!NOTDEF*/
4340         if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
4341     }
4342     j = 0;
4343     XtSetArg(args[j], XtNheight, commentH);  j++;
4344     XtSetArg(args[j], XtNwidth, commentW);  j++;
4345     XtSetArg(args[j], XtNx, commentX);  j++;
4346     XtSetArg(args[j], XtNy, commentY);  j++;
4347     XtSetValues(shell, args, j);
4348     XtSetKeyboardFocus(shell, edit);
4349
4350     return shell;
4351 }
4352
4353 /* Used for analysis window and ICS input window */
4354 Widget MiscCreate(name, text, mutable, callback, lines)
4355      char *name, *text;
4356      int /*Boolean*/ mutable;
4357      XtCallbackProc callback;
4358      int lines;
4359 {
4360     Arg args[16];
4361     Widget shell, layout, form, edit;
4362     Position x, y;
4363     Dimension bw_width, pw_height, ew_height, w, h;
4364     int j;
4365     int xx, yy;
4366     Window junk;
4367
4368     j = 0;
4369     XtSetArg(args[j], XtNresizable, True);  j++;
4370 #if TOPLEVEL
4371     shell =
4372       XtCreatePopupShell(name, topLevelShellWidgetClass,
4373                          shellWidget, args, j);
4374 #else
4375     shell =
4376       XtCreatePopupShell(name, transientShellWidgetClass,
4377                          shellWidget, args, j);
4378 #endif
4379     layout =
4380       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4381                             layoutArgs, XtNumber(layoutArgs));
4382     form =
4383       XtCreateManagedWidget("form", formWidgetClass, layout,
4384                             formArgs, XtNumber(formArgs));
4385
4386     j = 0;
4387     if (mutable) {
4388         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
4389         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
4390     }
4391     XtSetArg(args[j], XtNstring, text);  j++;
4392     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
4393     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
4394     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
4395     XtSetArg(args[j], XtNright, XtChainRight);  j++;
4396     XtSetArg(args[j], XtNresizable, True);  j++;
4397     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4398     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
4399     XtSetArg(args[j], XtNautoFill, True);  j++;
4400     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4401     edit =
4402       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4403
4404     XtRealizeWidget(shell);
4405
4406     j = 0;
4407     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
4408     XtGetValues(boardWidget, args, j);
4409
4410     j = 0;
4411     XtSetArg(args[j], XtNheight, &ew_height);  j++;
4412     XtGetValues(edit, args, j);
4413
4414     j = 0;
4415     XtSetArg(args[j], XtNheight, &pw_height);  j++;
4416     XtGetValues(shell, args, j);
4417     h = pw_height + (lines - 1) * ew_height;
4418     w = bw_width - 16;
4419
4420     XSync(xDisplay, False);
4421 #ifdef NOTDEF
4422     /* This code seems to tickle an X bug if it is executed too soon
4423        after xboard starts up.  The coordinates get transformed as if
4424        the main window was positioned at (0, 0).
4425     */
4426     XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
4427 #else  /*!NOTDEF*/
4428     XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4429                           RootWindowOfScreen(XtScreen(shellWidget)),
4430                           (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
4431 #endif /*!NOTDEF*/
4432     x = xx;
4433     y = yy;
4434     if (y < 0) y = 0; /*avoid positioning top offscreen*/
4435
4436     j = 0;
4437     XtSetArg(args[j], XtNheight, h);  j++;
4438     XtSetArg(args[j], XtNwidth, w);  j++;
4439     XtSetArg(args[j], XtNx, x);  j++;
4440     XtSetArg(args[j], XtNy, y);  j++;
4441     XtSetValues(shell, args, j);
4442
4443     return shell;
4444 }
4445
4446
4447 static int savedIndex;  /* gross that this is global */
4448
4449 void EditCommentPopUp(index, title, text)
4450      int index;
4451      char *title, *text;
4452 {
4453     Widget edit;
4454     Arg args[16];
4455     int j;
4456
4457     savedIndex = index;
4458     if (text == NULL) text = "";
4459
4460     if (editShell == NULL) {
4461         editShell =
4462           CommentCreate(title, text, True, EditCommentCallback, 4);
4463         XtRealizeWidget(editShell);
4464         CatchDeleteWindow(editShell, "EditCommentPopDown");
4465     } else {
4466         edit = XtNameToWidget(editShell, "*form.text");
4467         j = 0;
4468         XtSetArg(args[j], XtNstring, text); j++;
4469         XtSetValues(edit, args, j);
4470         j = 0;
4471         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4472         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4473         XtSetValues(editShell, args, j);
4474     }
4475
4476     XtPopup(editShell, XtGrabNone);
4477
4478     editUp = True;
4479     j = 0;
4480     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
4481     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
4482                 args, j);
4483 }
4484
4485 void EditCommentCallback(w, client_data, call_data)
4486      Widget w;
4487      XtPointer client_data, call_data;
4488 {
4489     String name, val;
4490     Arg args[16];
4491     int j;
4492     Widget edit;
4493
4494     j = 0;
4495     XtSetArg(args[j], XtNlabel, &name);  j++;
4496     XtGetValues(w, args, j);
4497
4498     if (strcmp(name, _("ok")) == 0) {
4499         edit = XtNameToWidget(editShell, "*form.text");
4500         j = 0;
4501         XtSetArg(args[j], XtNstring, &val); j++;
4502         XtGetValues(edit, args, j);
4503         ReplaceComment(savedIndex, val);
4504         EditCommentPopDown();
4505     } else if (strcmp(name, _("cancel")) == 0) {
4506         EditCommentPopDown();
4507     } else if (strcmp(name, _("clear")) == 0) {
4508         edit = XtNameToWidget(editShell, "*form.text");
4509         XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4510         XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4511     }
4512 }
4513
4514 void EditCommentPopDown()
4515 {
4516     Arg args[16];
4517     int j;
4518
4519     if (!editUp) return;
4520     j = 0;
4521     XtSetArg(args[j], XtNx, &commentX); j++;
4522     XtSetArg(args[j], XtNy, &commentY); j++;
4523     XtSetArg(args[j], XtNheight, &commentH); j++;
4524     XtSetArg(args[j], XtNwidth, &commentW); j++;
4525     XtGetValues(editShell, args, j);
4526     XtPopdown(editShell);
4527     editUp = False;
4528     j = 0;
4529     XtSetArg(args[j], XtNleftBitmap, None); j++;
4530     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
4531                 args, j);
4532 }
4533
4534 void ICSInputBoxPopUp()
4535 {
4536     Widget edit;
4537     Arg args[16];
4538     int j;
4539     char *title = _("ICS Input");
4540     XtTranslations tr;
4541
4542     if (ICSInputShell == NULL) {
4543         ICSInputShell = MiscCreate(title, "", True, NULL, 1);
4544         tr = XtParseTranslationTable(ICSInputTranslations);
4545         edit = XtNameToWidget(ICSInputShell, "*form.text");
4546         XtOverrideTranslations(edit, tr);
4547         XtRealizeWidget(ICSInputShell);
4548         CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
4549
4550     } else {
4551         edit = XtNameToWidget(ICSInputShell, "*form.text");
4552         j = 0;
4553         XtSetArg(args[j], XtNstring, ""); j++;
4554         XtSetValues(edit, args, j);
4555         j = 0;
4556         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4557         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4558         XtSetValues(ICSInputShell, args, j);
4559     }
4560
4561     XtPopup(ICSInputShell, XtGrabNone);
4562     XtSetKeyboardFocus(ICSInputShell, edit);
4563
4564     ICSInputBoxUp = True;
4565     j = 0;
4566     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
4567     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
4568                 args, j);
4569 }
4570
4571 void ICSInputSendText()
4572 {
4573     Widget edit;
4574     int j;
4575     Arg args[16];
4576     String val;
4577
4578     edit = XtNameToWidget(ICSInputShell, "*form.text");
4579     j = 0;
4580     XtSetArg(args[j], XtNstring, &val); j++;
4581     XtGetValues(edit, args, j);
4582     SendMultiLineToICS(val);
4583     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4584     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4585 }
4586
4587 void ICSInputBoxPopDown()
4588 {
4589     Arg args[16];
4590     int j;
4591
4592     if (!ICSInputBoxUp) return;
4593     j = 0;
4594     XtPopdown(ICSInputShell);
4595     ICSInputBoxUp = False;
4596     j = 0;
4597     XtSetArg(args[j], XtNleftBitmap, None); j++;
4598     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
4599                 args, j);
4600 }
4601
4602 void CommentPopUp(title, text)
4603      char *title, *text;
4604 {
4605     Arg args[16];
4606     int j;
4607     Widget edit;
4608
4609     if (commentShell == NULL) {
4610         commentShell =
4611           CommentCreate(title, text, False, CommentCallback, 4);
4612         XtRealizeWidget(commentShell);
4613         CatchDeleteWindow(commentShell, "CommentPopDown");
4614     } else {
4615         edit = XtNameToWidget(commentShell, "*form.text");
4616         j = 0;
4617         XtSetArg(args[j], XtNstring, text); j++;
4618         XtSetValues(edit, args, j);
4619         j = 0;
4620         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4621         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4622         XtSetValues(commentShell, args, j);
4623     }
4624
4625     XtPopup(commentShell, XtGrabNone);
4626     XSync(xDisplay, False);
4627
4628     commentUp = True;
4629 }
4630
4631 void CommentCallback(w, client_data, call_data)
4632      Widget w;
4633      XtPointer client_data, call_data;
4634 {
4635     String name;
4636     Arg args[16];
4637     int j;
4638
4639     j = 0;
4640     XtSetArg(args[j], XtNlabel, &name);  j++;
4641     XtGetValues(w, args, j);
4642
4643     if (strcmp(name, _("close")) == 0) {
4644         CommentPopDown();
4645     } else if (strcmp(name, _("edit")) == 0) {
4646         CommentPopDown();
4647         EditCommentEvent();
4648     }
4649 }
4650
4651
4652 void CommentPopDown()
4653 {
4654     Arg args[16];
4655     int j;
4656
4657     if (!commentUp) return;
4658     j = 0;
4659     XtSetArg(args[j], XtNx, &commentX); j++;
4660     XtSetArg(args[j], XtNy, &commentY); j++;
4661     XtSetArg(args[j], XtNwidth, &commentW); j++;
4662     XtSetArg(args[j], XtNheight, &commentH); j++;
4663     XtGetValues(commentShell, args, j);
4664     XtPopdown(commentShell);
4665     XSync(xDisplay, False);
4666     commentUp = False;
4667 }
4668
4669 void FileNamePopUp(label, def, proc, openMode)
4670      char *label;
4671      char *def;
4672      FileProc proc;
4673      char *openMode;
4674 {
4675     Arg args[16];
4676     Widget popup, layout, dialog, edit;
4677     Window root, child;
4678     int x, y, i;
4679     int win_x, win_y;
4680     unsigned int mask;
4681
4682     fileProc = proc;            /* I can't see a way not */
4683     fileOpenMode = openMode;    /*   to use globals here */
4684
4685     i = 0;
4686     XtSetArg(args[i], XtNresizable, True); i++;
4687     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4688     XtSetArg(args[i], XtNtitle, XtNewString(_("File name prompt"))); i++;
4689     fileNameShell = popup =
4690       XtCreatePopupShell("File name prompt", transientShellWidgetClass,
4691                          shellWidget, args, i);
4692
4693     layout =
4694       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4695                             layoutArgs, XtNumber(layoutArgs));
4696
4697     i = 0;
4698     XtSetArg(args[i], XtNlabel, label); i++;
4699     XtSetArg(args[i], XtNvalue, def); i++;
4700     XtSetArg(args[i], XtNborderWidth, 0); i++;
4701     dialog = XtCreateManagedWidget("fileName", dialogWidgetClass,
4702                                    layout, args, i);
4703
4704     XawDialogAddButton(dialog, _("ok"), FileNameCallback, (XtPointer) dialog);
4705     XawDialogAddButton(dialog, _("cancel"), FileNameCallback,
4706                        (XtPointer) dialog);
4707
4708     XtRealizeWidget(popup);
4709     CatchDeleteWindow(popup, "FileNamePopDown");
4710
4711     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4712                   &x, &y, &win_x, &win_y, &mask);
4713
4714     XtSetArg(args[0], XtNx, x - 10);
4715     XtSetArg(args[1], XtNy, y - 30);
4716     XtSetValues(popup, args, 2);
4717
4718     XtPopup(popup, XtGrabExclusive);
4719     filenameUp = True;
4720
4721     edit = XtNameToWidget(dialog, "*value");
4722     XtSetKeyboardFocus(popup, edit);
4723 }
4724
4725 void FileNamePopDown()
4726 {
4727     if (!filenameUp) return;
4728     XtPopdown(fileNameShell);
4729     XtDestroyWidget(fileNameShell);
4730     filenameUp = False;
4731     ModeHighlight();
4732 }
4733
4734 void FileNameCallback(w, client_data, call_data)
4735      Widget w;
4736      XtPointer client_data, call_data;
4737 {
4738     String name;
4739     Arg args[16];
4740
4741     XtSetArg(args[0], XtNlabel, &name);
4742     XtGetValues(w, args, 1);
4743
4744     if (strcmp(name, _("cancel")) == 0) {
4745         FileNamePopDown();
4746         return;
4747     }
4748
4749     FileNameAction(w, NULL, NULL, NULL);
4750 }
4751
4752 void FileNameAction(w, event, prms, nprms)
4753      Widget w;
4754      XEvent *event;
4755      String *prms;
4756      Cardinal *nprms;
4757 {
4758     char buf[MSG_SIZ];
4759     String name;
4760     FILE *f;
4761     char *p, *fullname;
4762     int index;
4763
4764     name = XawDialogGetValueString(w = XtParent(w));
4765
4766     if ((name != NULL) && (*name != NULLCHAR)) {
4767         strcpy(buf, name);
4768         XtPopdown(w = XtParent(XtParent(w)));
4769         XtDestroyWidget(w);
4770         filenameUp = False;
4771
4772         p = strrchr(buf, ' ');
4773         if (p == NULL) {
4774             index = 0;
4775         } else {
4776             *p++ = NULLCHAR;
4777             index = atoi(p);
4778         }
4779         fullname = ExpandPathName(buf);
4780         if (!fullname) {
4781             ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
4782         }
4783         else {
4784             f = fopen(fullname, fileOpenMode);
4785             if (f == NULL) {
4786                 DisplayError(_("Failed to open file"), errno);
4787             } else {
4788                 (void) (*fileProc)(f, index, buf);
4789             }
4790         }
4791         ModeHighlight();
4792         return;
4793     }
4794
4795     XtPopdown(w = XtParent(XtParent(w)));
4796     XtDestroyWidget(w);
4797     filenameUp = False;
4798     ModeHighlight();
4799 }
4800
4801 void PromotionPopUp()
4802 {
4803     Arg args[16];
4804     Widget dialog, layout;
4805     Position x, y;
4806     Dimension bw_width, pw_width;
4807     int j;
4808
4809     j = 0;
4810     XtSetArg(args[j], XtNwidth, &bw_width); j++;
4811     XtGetValues(boardWidget, args, j);
4812
4813     j = 0;
4814     XtSetArg(args[j], XtNresizable, True); j++;
4815     XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
4816     promotionShell =
4817       XtCreatePopupShell("Promotion", transientShellWidgetClass,
4818                          shellWidget, args, j);
4819     layout =
4820       XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
4821                             layoutArgs, XtNumber(layoutArgs));
4822
4823     j = 0;
4824     XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
4825     XtSetArg(args[j], XtNborderWidth, 0); j++;
4826     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
4827                                    layout, args, j);
4828
4829   if(gameInfo.variant != VariantShogi) {
4830     XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
4831                        (XtPointer) dialog);
4832     XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
4833                        (XtPointer) dialog);
4834     XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
4835                        (XtPointer) dialog);
4836     XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
4837                        (XtPointer) dialog);
4838     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
4839         gameInfo.variant == VariantGiveaway) {
4840       XawDialogAddButton(dialog, _("King"), PromotionCallback,
4841                          (XtPointer) dialog);
4842     }
4843     if(gameInfo.variant == VariantCapablanca ||
4844        gameInfo.variant == VariantGothic ||
4845        gameInfo.variant == VariantCapaRandom) {
4846       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
4847                          (XtPointer) dialog);
4848       XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
4849                          (XtPointer) dialog);
4850     }
4851   } else // [HGM] shogi
4852   {
4853       XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
4854                          (XtPointer) dialog);
4855       XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
4856                          (XtPointer) dialog);
4857   }
4858     XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
4859                        (XtPointer) dialog);
4860
4861     XtRealizeWidget(promotionShell);
4862     CatchDeleteWindow(promotionShell, "PromotionPopDown");
4863
4864     j = 0;
4865     XtSetArg(args[j], XtNwidth, &pw_width); j++;
4866     XtGetValues(promotionShell, args, j);
4867
4868     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4869                       lineGap + squareSize/3 +
4870                       ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
4871                        0 : 6*(squareSize + lineGap)), &x, &y);
4872
4873     j = 0;
4874     XtSetArg(args[j], XtNx, x); j++;
4875     XtSetArg(args[j], XtNy, y); j++;
4876     XtSetValues(promotionShell, args, j);
4877
4878     XtPopup(promotionShell, XtGrabNone);
4879
4880     promotionUp = True;
4881 }
4882
4883 void PromotionPopDown()
4884 {
4885     if (!promotionUp) return;
4886     XtPopdown(promotionShell);
4887     XtDestroyWidget(promotionShell);
4888     promotionUp = False;
4889 }
4890
4891 void PromotionCallback(w, client_data, call_data)
4892      Widget w;
4893      XtPointer client_data, call_data;
4894 {
4895     String name;
4896     Arg args[16];
4897     int promoChar;
4898
4899     XtSetArg(args[0], XtNlabel, &name);
4900     XtGetValues(w, args, 1);
4901
4902     PromotionPopDown();
4903
4904     if (fromX == -1) return;
4905
4906     if (strcmp(name, _("cancel")) == 0) {
4907         fromX = fromY = -1;
4908         ClearHighlights();
4909         return;
4910     } else if (strcmp(name, _("Knight")) == 0) {
4911         promoChar = 'n';
4912     } else if (strcmp(name, _("Promote")) == 0) {
4913         promoChar = '+';
4914     } else if (strcmp(name, _("Defer")) == 0) {
4915         promoChar = '=';
4916     } else {
4917         promoChar = ToLower(name[0]);
4918     }
4919
4920     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);
4921
4922     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
4923     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
4924     fromX = fromY = -1;
4925 }
4926
4927
4928 void ErrorCallback(w, client_data, call_data)
4929      Widget w;
4930      XtPointer client_data, call_data;
4931 {
4932     errorUp = False;
4933     XtPopdown(w = XtParent(XtParent(XtParent(w))));
4934     XtDestroyWidget(w);
4935     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4936 }
4937
4938
4939 void ErrorPopDown()
4940 {
4941     if (!errorUp) return;
4942     errorUp = False;
4943
4944     if(GUI_Error)
4945       gtk_widget_destroy(GTK_WIDGET(GUI_Error));
4946
4947     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4948
4949     return;
4950 }
4951
4952 void ErrorPopUp(title, label, modal)
4953      char *title, *label;
4954      int modal;
4955 {
4956   GUI_Error = gtk_message_dialog_new(GTK_WINDOW(GUI_Window),
4957                                   GTK_DIALOG_DESTROY_WITH_PARENT,
4958                                   GTK_MESSAGE_ERROR,
4959                                   GTK_BUTTONS_CLOSE,
4960                                   (gchar *)label);
4961
4962   gtk_window_set_title(GTK_WINDOW(GUI_Error),(gchar *) title);
4963   if(modal)
4964     {
4965       gtk_dialog_run(GTK_DIALOG(GUI_Error));
4966       gtk_widget_destroy(GTK_WIDGET(GUI_Error));
4967     }
4968   else
4969     {
4970       g_signal_connect_swapped (GUI_Error, "response",
4971                                 G_CALLBACK (ErrorPopDownProc),
4972                                 GUI_Error);
4973       errorUp = True;
4974       gtk_widget_show(GTK_WIDGET(GUI_Error));
4975     }
4976
4977   return;
4978 }
4979
4980 /* Disable all user input other than deleting the window */
4981 static int frozen = 0;
4982 void FreezeUI()
4983 {
4984   if (frozen) return;
4985   /* Grab by a widget that doesn't accept input */
4986   //  XtAddGrab(messageWidget, TRUE, FALSE);
4987   frozen = 1;
4988 }
4989
4990 /* Undo a FreezeUI */
4991 void ThawUI()
4992 {
4993   if (!frozen) return;
4994   //  XtRemoveGrab(messageWidget);
4995   frozen = 0;
4996 }
4997
4998 char *ModeToWidgetName(mode)
4999      GameMode mode;
5000 {
5001     switch (mode) {
5002       case BeginningOfGame:
5003         if (appData.icsActive)
5004           return "menuMode.ICS Client";
5005         else if (appData.noChessProgram ||
5006                  *appData.cmailGameName != NULLCHAR)
5007           return "menuMode.Edit Game";
5008         else
5009           return "menuMode.Machine Black";
5010       case MachinePlaysBlack:
5011         return "menuMode.Machine Black";
5012       case MachinePlaysWhite:
5013         return "menuMode.Machine White";
5014       case AnalyzeMode:
5015         return "menuMode.Analysis Mode";
5016       case AnalyzeFile:
5017         return "menuMode.Analyze File";
5018       case TwoMachinesPlay:
5019         return "menuMode.Two Machines";
5020       case EditGame:
5021         return "menuMode.Edit Game";
5022       case PlayFromGameFile:
5023         return "menuFile.Load Game";
5024       case EditPosition:
5025         return "menuMode.Edit Position";
5026       case Training:
5027         return "menuMode.Training";
5028       case IcsPlayingWhite:
5029       case IcsPlayingBlack:
5030       case IcsObserving:
5031       case IcsIdle:
5032       case IcsExamining:
5033         return "menuMode.ICS Client";
5034       default:
5035       case EndOfGame:
5036         return NULL;
5037     }
5038 }
5039
5040 void ModeHighlight()
5041 {
5042     static int oldPausing = FALSE;
5043     static GameMode oldmode = (GameMode) -1;
5044     char *wname;
5045
5046    // todo this toggling of the pause button doesn't seem to work?
5047     // e.g. select pause from buttonbar doesn't activate menumode.pause
5048
5049     //    if (!boardWidget || !XtIsRealized(boardWidget)) return;
5050
5051     if (pausing != oldPausing) {
5052       oldPausing = pausing;
5053       gtk_button_set_relief(GTK_BUTTON (gtk_builder_get_object (builder, "menuMode.Pause")),pausing?GTK_RELIEF_NORMAL:GTK_RELIEF_NONE);
5054       /* toggle background color in showbuttonbar */
5055       if (appData.showButtonBar) {
5056         if (pausing) {
5057           gtk_button_pressed(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
5058         } else {
5059           gtk_button_released(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
5060         }
5061       }
5062     }
5063
5064     wname = ModeToWidgetName(oldmode);
5065     if(wname)
5066        gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, wname)),True);
5067
5068     oldmode = gameMode;
5069
5070     /* Maybe all the enables should be handled here, not just this one */
5071     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuMode.Training")),
5072                              gameMode == Training || gameMode == PlayFromGameFile);
5073 }
5074
5075
5076 /*
5077  * Button/menu procedures
5078  */
5079
5080 int LoadGamePopUp(f, gameNumber, title)
5081      FILE *f;
5082      int gameNumber;
5083      char *title;
5084 {
5085     cmailMsgLoaded = FALSE;
5086
5087     if (gameNumber == 0) 
5088       {
5089         int error = GameListBuild(f);
5090
5091         if (error) 
5092           {
5093             DisplayError(_("Cannot build game list"), error);
5094           } 
5095         else if (!ListEmpty(&gameList) 
5096                  && ((ListGame *) gameList.tailPred)->number > 1) 
5097           {
5098             GameListPopUp(f, title);
5099             return TRUE;
5100           };
5101
5102         GameListDestroy();
5103         gameNumber = 1;
5104       };
5105
5106     return LoadGame(f, gameNumber, title, FALSE);
5107 }
5108
5109 void LoadPositionProc(w, event, prms, nprms)
5110      Widget w;
5111      XEvent *event;
5112      String *prms;
5113      Cardinal *nprms;
5114 {
5115     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5116         Reset(FALSE, TRUE);
5117     }
5118     FileNamePopUp(_("Load position file name?"), "", LoadPosition, "rb");
5119 }
5120
5121 void SaveGameProc(w, event, prms, nprms)
5122      Widget w;
5123      XEvent *event;
5124      String *prms;
5125      Cardinal *nprms;
5126 {
5127     FileNamePopUp(_("Save game file name?"),
5128                   DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5129                   SaveGame, "a");
5130 }
5131
5132 void SavePositionProc(w, event, prms, nprms)
5133      Widget w;
5134      XEvent *event;
5135      String *prms;
5136      Cardinal *nprms;
5137 {
5138     FileNamePopUp(_("Save position file name?"),
5139                   DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5140                   SavePosition, "a");
5141 }
5142
5143 void ReloadCmailMsgProc(w, event, prms, nprms)
5144      Widget w;
5145      XEvent *event;
5146      String *prms;
5147      Cardinal *nprms;
5148 {
5149     ReloadCmailMsgEvent(FALSE);
5150 }
5151
5152 void MailMoveProc(w, event, prms, nprms)
5153      Widget w;
5154      XEvent *event;
5155      String *prms;
5156      Cardinal *nprms;
5157 {
5158     MailMoveEvent();
5159 }
5160
5161 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5162 static char *selected_fen_position=NULL;
5163
5164 static Boolean
5165 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5166                  Atom *type_return, XtPointer *value_return,
5167                  unsigned long *length_return, int *format_return)
5168 {
5169   char *selection_tmp;
5170
5171   if (!selected_fen_position) return False; /* should never happen */
5172   if (*target == XA_STRING){
5173     /* note: since no XtSelectionDoneProc was registered, Xt will
5174      * automatically call XtFree on the value returned.  So have to
5175      * make a copy of it allocated with XtMalloc */
5176     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5177     strcpy(selection_tmp, selected_fen_position);
5178
5179     *value_return=selection_tmp;
5180     *length_return=strlen(selection_tmp);
5181     *type_return=XA_STRING;
5182     *format_return = 8; /* bits per byte */
5183     return True;
5184   } else {
5185     return False;
5186   }
5187 }
5188
5189 /* note: when called from menu all parameters are NULL, so no clue what the
5190  * Widget which was clicked on was, or what the click event was
5191  */
5192 void CopyPositionProc(w, event, prms, nprms)
5193   Widget w;
5194   XEvent *event;
5195   String *prms;
5196   Cardinal *nprms;
5197   {
5198     int ret;
5199
5200     if (selected_fen_position) free(selected_fen_position);
5201     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5202     if (!selected_fen_position) return;
5203     ret = XtOwnSelection(menuBarWidget, XA_PRIMARY,
5204                          CurrentTime,
5205                          SendPositionSelection,
5206                          NULL/* lose_ownership_proc */ ,
5207                          NULL/* transfer_done_proc */);
5208     if (!ret) {
5209       free(selected_fen_position);
5210       selected_fen_position=NULL;
5211     }
5212   }
5213
5214 /* function called when the data to Paste is ready */
5215 static void
5216 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5217            Atom *type, XtPointer value, unsigned long *len, int *format)
5218 {
5219   char *fenstr=value;
5220   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5221   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5222   EditPositionPasteFEN(fenstr);
5223   XtFree(value);
5224 }
5225
5226 /* called when Paste Position button is pressed,
5227  * all parameters will be NULL */
5228 void PastePositionProc(w, event, prms, nprms)
5229   Widget w;
5230   XEvent *event;
5231   String *prms;
5232   Cardinal *nprms;
5233 {
5234     XtGetSelectionValue(menuBarWidget, XA_PRIMARY, XA_STRING,
5235       /* (XtSelectionCallbackProc) */ PastePositionCB,
5236       NULL, /* client_data passed to PastePositionCB */
5237
5238       /* better to use the time field from the event that triggered the
5239        * call to this function, but that isn't trivial to get
5240        */
5241       CurrentTime
5242     );
5243     return;
5244 }
5245
5246 static Boolean
5247 SendGameSelection(Widget w, Atom *selection, Atom *target,
5248                   Atom *type_return, XtPointer *value_return,
5249                   unsigned long *length_return, int *format_return)
5250 {
5251   char *selection_tmp;
5252
5253   if (*target == XA_STRING){
5254     FILE* f = fopen(gameCopyFilename, "r");
5255     long len;
5256     size_t count;
5257     if (f == NULL) return False;
5258     fseek(f, 0, 2);
5259     len = ftell(f);
5260     rewind(f);
5261     selection_tmp = XtMalloc(len + 1);
5262     count = fread(selection_tmp, 1, len, f);
5263     if (len != count) {
5264       XtFree(selection_tmp);
5265       return False;
5266     }
5267     selection_tmp[len] = NULLCHAR;
5268     *value_return = selection_tmp;
5269     *length_return = len;
5270     *type_return = XA_STRING;
5271     *format_return = 8; /* bits per byte */
5272     return True;
5273   } else {
5274     return False;
5275   }
5276 }
5277
5278 /* note: when called from menu all parameters are NULL, so no clue what the
5279  * Widget which was clicked on was, or what the click event was
5280  */
5281 void CopyGameProc(w, event, prms, nprms)
5282   Widget w;
5283   XEvent *event;
5284   String *prms;
5285   Cardinal *nprms;
5286 {
5287   int ret;
5288
5289   ret = SaveGameToFile(gameCopyFilename, FALSE);
5290   if (!ret) return;
5291
5292   ret = XtOwnSelection(menuBarWidget, XA_PRIMARY,
5293                        CurrentTime,
5294                        SendGameSelection,
5295                        NULL/* lose_ownership_proc */ ,
5296                        NULL/* transfer_done_proc */);
5297 }
5298
5299 /* function called when the data to Paste is ready */
5300 static void
5301 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5302             Atom *type, XtPointer value, unsigned long *len, int *format)
5303 {
5304   FILE* f;
5305   if (value == NULL || *len == 0) {
5306     return; /* nothing had been selected to copy */
5307   }
5308   f = fopen(gamePasteFilename, "w");
5309   if (f == NULL) {
5310     DisplayError(_("Can't open temp file"), errno);
5311     return;
5312   }
5313   fwrite(value, 1, *len, f);
5314   fclose(f);
5315   XtFree(value);
5316   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5317 }
5318
5319 /* called when Paste Game button is pressed,
5320  * all parameters will be NULL */
5321 void PasteGameProc(w, event, prms, nprms)
5322   Widget w;
5323   XEvent *event;
5324   String *prms;
5325   Cardinal *nprms;
5326 {
5327     XtGetSelectionValue(menuBarWidget, XA_PRIMARY, XA_STRING,
5328       /* (XtSelectionCallbackProc) */ PasteGameCB,
5329       NULL, /* client_data passed to PasteGameCB */
5330
5331       /* better to use the time field from the event that triggered the
5332        * call to this function, but that isn't trivial to get
5333        */
5334       CurrentTime
5335     );
5336     return;
5337 }
5338
5339
5340 void AutoSaveGame()
5341 {
5342     SaveGameProc(NULL, NULL, NULL, NULL);
5343 }
5344
5345 void AnalyzeModeProc(w, event, prms, nprms)
5346      Widget w;
5347      XEvent *event;
5348      String *prms;
5349      Cardinal *nprms;
5350 {
5351     char buf[MSG_SIZ];
5352
5353     if (!first.analysisSupport) {
5354       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5355       DisplayError(buf, 0);
5356       return;
5357     }
5358     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5359     if (appData.icsActive) {
5360         if (gameMode != IcsObserving) {
5361             sprintf(buf,_("You are not observing a game"));
5362             DisplayError(buf, 0);
5363             /* secure check */
5364             if (appData.icsEngineAnalyze) {
5365                 if (appData.debugMode)
5366                     fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5367                 ExitAnalyzeMode();
5368                 ModeHighlight();
5369             }
5370             return;
5371         }
5372         /* if enable, use want disable icsEngineAnalyze */
5373         if (appData.icsEngineAnalyze) {
5374                 ExitAnalyzeMode();
5375                 ModeHighlight();
5376                 return;
5377         }
5378         appData.icsEngineAnalyze = TRUE;
5379         if (appData.debugMode)
5380             fprintf(debugFP, _("ICS engine analyze starting... \n"));
5381     }
5382     if (!appData.showThinking)
5383       ShowThinkingProc(NULL,NULL);
5384
5385     AnalyzeModeEvent();
5386 }
5387
5388 void AnalyzeFileProc(w, event, prms, nprms)
5389      Widget w;
5390      XEvent *event;
5391      String *prms;
5392      Cardinal *nprms;
5393 {
5394     if (!first.analysisSupport) {
5395       char buf[MSG_SIZ];
5396       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5397       DisplayError(buf, 0);
5398       return;
5399     }
5400     Reset(FALSE, TRUE);
5401
5402     if (!appData.showThinking)
5403       ShowThinkingProc(NULL,NULL);
5404
5405     AnalyzeFileEvent();
5406     FileNamePopUp(_("File to analyze"), "", LoadGamePopUp, "rb");
5407     AnalysisPeriodicEvent(1);
5408 }
5409
5410
5411 void EditGameProc(w, event, prms, nprms)
5412      Widget w;
5413      XEvent *event;
5414      String *prms;
5415      Cardinal *nprms;
5416 {
5417     EditGameEvent();
5418 }
5419
5420 void EditPositionProc(w, event, prms, nprms)
5421      Widget w;
5422      XEvent *event;
5423      String *prms;
5424      Cardinal *nprms;
5425 {
5426     EditPositionEvent();
5427 }
5428
5429 void TrainingProc(w, event, prms, nprms)
5430      Widget w;
5431      XEvent *event;
5432      String *prms;
5433      Cardinal *nprms;
5434 {
5435     TrainingEvent();
5436 }
5437
5438 void EditCommentProc(w, event, prms, nprms)
5439      Widget w;
5440      XEvent *event;
5441      String *prms;
5442      Cardinal *nprms;
5443 {
5444     if (editUp) {
5445         EditCommentPopDown();
5446     } else {
5447         EditCommentEvent();
5448     }
5449 }
5450
5451 void IcsInputBoxProc(w, event, prms, nprms)
5452      Widget w;
5453      XEvent *event;
5454      String *prms;
5455      Cardinal *nprms;
5456 {
5457     if (ICSInputBoxUp) {
5458         ICSInputBoxPopDown();
5459     } else {
5460         ICSInputBoxPopUp();
5461     }
5462 }
5463
5464
5465 void EnterKeyProc(w, event, prms, nprms)
5466      Widget w;
5467      XEvent *event;
5468      String *prms;
5469      Cardinal *nprms;
5470 {
5471     if (ICSInputBoxUp == True)
5472       ICSInputSendText();
5473 }
5474
5475 void AlwaysQueenProc(w, event, prms, nprms)
5476      Widget w;
5477      XEvent *event;
5478      String *prms;
5479      Cardinal *nprms;
5480 {
5481     Arg args[16];
5482
5483     appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
5484
5485     if (appData.alwaysPromoteToQueen) {
5486         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5487     } else {
5488         XtSetArg(args[0], XtNleftBitmap, None);
5489     }
5490     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
5491                 args, 1);
5492 }
5493
5494 void AnimateDraggingProc(w, event, prms, nprms)
5495      Widget w;
5496      XEvent *event;
5497      String *prms;
5498      Cardinal *nprms;
5499 {
5500     Arg args[16];
5501
5502     appData.animateDragging = !appData.animateDragging;
5503
5504     if (appData.animateDragging) {
5505         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5506         CreateAnimVars();
5507     } else {
5508         XtSetArg(args[0], XtNleftBitmap, None);
5509     }
5510     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
5511                 args, 1);
5512 }
5513
5514 void AnimateMovingProc(w, event, prms, nprms)
5515      Widget w;
5516      XEvent *event;
5517      String *prms;
5518      Cardinal *nprms;
5519 {
5520     Arg args[16];
5521
5522     appData.animate = !appData.animate;
5523
5524     if (appData.animate) {
5525         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5526         CreateAnimVars();
5527     } else {
5528         XtSetArg(args[0], XtNleftBitmap, None);
5529     }
5530     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
5531                 args, 1);
5532 }
5533
5534 void AutobsProc(w, event, prms, nprms)
5535      Widget w;
5536      XEvent *event;
5537      String *prms;
5538      Cardinal *nprms;
5539 {
5540     Arg args[16];
5541
5542     appData.autoObserve = !appData.autoObserve;
5543
5544     if (appData.autoObserve) {
5545         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5546     } else {
5547         XtSetArg(args[0], XtNleftBitmap, None);
5548     }
5549     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
5550                 args, 1);
5551 }
5552
5553 void AutoraiseProc(w, event, prms, nprms)
5554      Widget w;
5555      XEvent *event;
5556      String *prms;
5557      Cardinal *nprms;
5558 {
5559     Arg args[16];
5560
5561     appData.autoRaiseBoard = !appData.autoRaiseBoard;
5562
5563     if (appData.autoRaiseBoard) {
5564         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5565     } else {
5566         XtSetArg(args[0], XtNleftBitmap, None);
5567     }
5568     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Raise Board"),
5569                 args, 1);
5570 }
5571
5572 void AutosaveProc(w, event, prms, nprms)
5573      Widget w;
5574      XEvent *event;
5575      String *prms;
5576      Cardinal *nprms;
5577 {
5578     Arg args[16];
5579
5580     appData.autoSaveGames = !appData.autoSaveGames;
5581
5582     if (appData.autoSaveGames) {
5583         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5584     } else {
5585         XtSetArg(args[0], XtNleftBitmap, None);
5586     }
5587     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
5588                 args, 1);
5589 }
5590
5591 void BlindfoldProc(w, event, prms, nprms)
5592      Widget w;
5593      XEvent *event;
5594      String *prms;
5595      Cardinal *nprms;
5596 {
5597     Arg args[16];
5598
5599     appData.blindfold = !appData.blindfold;
5600
5601     if (appData.blindfold) {
5602         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5603     } else {
5604         XtSetArg(args[0], XtNleftBitmap, None);
5605     }
5606     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
5607                 args, 1);
5608
5609     DrawPosition(True, NULL);
5610 }
5611
5612 void TestLegalityProc(w, event, prms, nprms)
5613      Widget w;
5614      XEvent *event;
5615      String *prms;
5616      Cardinal *nprms;
5617 {
5618     Arg args[16];
5619
5620     appData.testLegality = !appData.testLegality;
5621
5622     if (appData.testLegality) {
5623         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5624     } else {
5625         XtSetArg(args[0], XtNleftBitmap, None);
5626     }
5627     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
5628                 args, 1);
5629 }
5630
5631
5632 void FlashMovesProc(w, event, prms, nprms)
5633      Widget w;
5634      XEvent *event;
5635      String *prms;
5636      Cardinal *nprms;
5637 {
5638     Arg args[16];
5639
5640     if (appData.flashCount == 0) {
5641         appData.flashCount = 3;
5642     } else {
5643         appData.flashCount = -appData.flashCount;
5644     }
5645
5646     if (appData.flashCount > 0) {
5647         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5648     } else {
5649         XtSetArg(args[0], XtNleftBitmap, None);
5650     }
5651     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
5652                 args, 1);
5653 }
5654
5655 #if HIGHDRAG
5656 void HighlightDraggingProc(w, event, prms, nprms)
5657      Widget w;
5658      XEvent *event;
5659      String *prms;
5660      Cardinal *nprms;
5661 {
5662     Arg args[16];
5663
5664     appData.highlightDragging = !appData.highlightDragging;
5665
5666     if (appData.highlightDragging) {
5667         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5668     } else {
5669         XtSetArg(args[0], XtNleftBitmap, None);
5670     }
5671     XtSetValues(XtNameToWidget(menuBarWidget,
5672                                "menuOptions.Highlight Dragging"), args, 1);
5673 }
5674 #endif
5675
5676 void HighlightLastMoveProc(w, event, prms, nprms)
5677      Widget w;
5678      XEvent *event;
5679      String *prms;
5680      Cardinal *nprms;
5681 {
5682     Arg args[16];
5683
5684     appData.highlightLastMove = !appData.highlightLastMove;
5685
5686     if (appData.highlightLastMove) {
5687         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5688     } else {
5689         XtSetArg(args[0], XtNleftBitmap, None);
5690     }
5691     XtSetValues(XtNameToWidget(menuBarWidget,
5692                                "menuOptions.Highlight Last Move"), args, 1);
5693 }
5694
5695 void IcsAlarmProc(w, event, prms, nprms)
5696      Widget w;
5697      XEvent *event;
5698      String *prms;
5699      Cardinal *nprms;
5700 {
5701     Arg args[16];
5702
5703     appData.icsAlarm = !appData.icsAlarm;
5704
5705     if (appData.icsAlarm) {
5706         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5707     } else {
5708         XtSetArg(args[0], XtNleftBitmap, None);
5709     }
5710     XtSetValues(XtNameToWidget(menuBarWidget,
5711                                "menuOptions.ICS Alarm"), args, 1);
5712 }
5713
5714 void MoveSoundProc(w, event, prms, nprms)
5715      Widget w;
5716      XEvent *event;
5717      String *prms;
5718      Cardinal *nprms;
5719 {
5720     Arg args[16];
5721
5722     appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
5723
5724     if (appData.ringBellAfterMoves) {
5725         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5726     } else {
5727         XtSetArg(args[0], XtNleftBitmap, None);
5728     }
5729     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
5730                 args, 1);
5731 }
5732
5733
5734 void OldSaveStyleProc(w, event, prms, nprms)
5735      Widget w;
5736      XEvent *event;
5737      String *prms;
5738      Cardinal *nprms;
5739 {
5740     Arg args[16];
5741
5742     appData.oldSaveStyle = !appData.oldSaveStyle;
5743
5744     if (appData.oldSaveStyle) {
5745         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5746     } else {
5747         XtSetArg(args[0], XtNleftBitmap, None);
5748     }
5749     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Old Save Style"),
5750                 args, 1);
5751 }
5752
5753 void PeriodicUpdatesProc(w, event, prms, nprms)
5754      Widget w;
5755      XEvent *event;
5756      String *prms;
5757      Cardinal *nprms;
5758 {
5759     Arg args[16];
5760
5761     PeriodicUpdatesEvent(!appData.periodicUpdates);
5762
5763     if (appData.periodicUpdates) {
5764         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5765     } else {
5766         XtSetArg(args[0], XtNleftBitmap, None);
5767     }
5768     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
5769                 args, 1);
5770 }
5771
5772 void PonderNextMoveProc(w, event, prms, nprms)
5773      Widget w;
5774      XEvent *event;
5775      String *prms;
5776      Cardinal *nprms;
5777 {
5778     Arg args[16];
5779
5780     PonderNextMoveEvent(!appData.ponderNextMove);
5781
5782     if (appData.ponderNextMove) {
5783         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5784     } else {
5785         XtSetArg(args[0], XtNleftBitmap, None);
5786     }
5787     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
5788                 args, 1);
5789 }
5790
5791 void PopupExitMessageProc(w, event, prms, nprms)
5792      Widget w;
5793      XEvent *event;
5794      String *prms;
5795      Cardinal *nprms;
5796 {
5797     Arg args[16];
5798
5799     appData.popupExitMessage = !appData.popupExitMessage;
5800
5801     if (appData.popupExitMessage) {
5802         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5803     } else {
5804         XtSetArg(args[0], XtNleftBitmap, None);
5805     }
5806     XtSetValues(XtNameToWidget(menuBarWidget,
5807                                "menuOptions.Popup Exit Message"), args, 1);
5808 }
5809
5810 void PopupMoveErrorsProc(w, event, prms, nprms)
5811      Widget w;
5812      XEvent *event;
5813      String *prms;
5814      Cardinal *nprms;
5815 {
5816     Arg args[16];
5817
5818     appData.popupMoveErrors = !appData.popupMoveErrors;
5819
5820     if (appData.popupMoveErrors) {
5821         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5822     } else {
5823         XtSetArg(args[0], XtNleftBitmap, None);
5824     }
5825     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
5826                 args, 1);
5827 }
5828
5829 void PremoveProc(w, event, prms, nprms)
5830      Widget w;
5831      XEvent *event;
5832      String *prms;
5833      Cardinal *nprms;
5834 {
5835     Arg args[16];
5836
5837     appData.premove = !appData.premove;
5838
5839     if (appData.premove) {
5840         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5841     } else {
5842         XtSetArg(args[0], XtNleftBitmap, None);
5843     }
5844     XtSetValues(XtNameToWidget(menuBarWidget,
5845                                "menuOptions.Premove"), args, 1);
5846 }
5847
5848 void QuietPlayProc(w, event, prms, nprms)
5849      Widget w;
5850      XEvent *event;
5851      String *prms;
5852      Cardinal *nprms;
5853 {
5854     Arg args[16];
5855
5856     appData.quietPlay = !appData.quietPlay;
5857
5858     if (appData.quietPlay) {
5859         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5860     } else {
5861         XtSetArg(args[0], XtNleftBitmap, None);
5862     }
5863     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Quiet Play"),
5864                 args, 1);
5865 }
5866
5867 void DebugProc(w, event, prms, nprms)
5868      Widget w;
5869      XEvent *event;
5870      String *prms;
5871      Cardinal *nprms;
5872 {
5873     appData.debugMode = !appData.debugMode;
5874 }
5875
5876 void AboutGameProc(w, event, prms, nprms)
5877      Widget w;
5878      XEvent *event;
5879      String *prms;
5880      Cardinal *nprms;
5881 {
5882     AboutGameEvent();
5883 }
5884
5885 void NothingProc(w, event, prms, nprms)
5886      Widget w;
5887      XEvent *event;
5888      String *prms;
5889      Cardinal *nprms;
5890 {
5891     return;
5892 }
5893
5894 void Iconify(w, event, prms, nprms)
5895      Widget w;
5896      XEvent *event;
5897      String *prms;
5898      Cardinal *nprms;
5899 {
5900     Arg args[16];
5901
5902     fromX = fromY = -1;
5903     XtSetArg(args[0], XtNiconic, True);
5904     XtSetValues(shellWidget, args, 1);
5905 }
5906
5907 void DisplayMessage(message, extMessage)
5908      gchar *message, *extMessage;
5909 {
5910     char buf[MSG_SIZ];
5911     Arg arg;
5912
5913     if (extMessage) {
5914         if (*message) {
5915             snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
5916             message = buf;
5917         } else {
5918             message = extMessage;
5919         }
5920     }
5921     gtk_label_set_text( GTK_LABEL(gtk_builder_get_object (builder, "Messages")),message);
5922
5923     return;
5924 }
5925
5926 void DisplayTitle(text)
5927      char *text;
5928 {
5929     gchar title[MSG_SIZ];
5930
5931     if (text == NULL) text = "";
5932
5933     if (appData.titleInWindow)
5934       {
5935         /* TODO */
5936       }
5937
5938     if (*text != NULLCHAR)
5939       {
5940         strcpy(title, text);
5941       }
5942     else if (appData.icsActive)
5943       {
5944         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
5945       }
5946     else if (appData.cmailGameName[0] != NULLCHAR)
5947       {
5948         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
5949 #ifdef GOTHIC
5950     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
5951       }
5952     else if (gameInfo.variant == VariantGothic)
5953       {
5954         strcpy(title, GOTHIC);
5955 #endif
5956 #ifdef FALCON
5957       }
5958     else if (gameInfo.variant == VariantFalcon)
5959       {
5960         strcpy(title, FALCON);
5961 #endif
5962       }
5963     else if (appData.noChessProgram)
5964       {
5965         strcpy(title, programName);
5966       }
5967     else
5968       {
5969         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
5970       }
5971     gtk_window_set_title(GTK_WINDOW(GUI_Window),title);
5972
5973     return;
5974 }
5975
5976
5977 void DisplayError(message, error)
5978      String message;
5979      int error;
5980 {
5981     char buf[MSG_SIZ];
5982
5983     if (error == 0) {
5984         if (appData.debugMode || appData.matchMode) {
5985             fprintf(stderr, "%s: %s\n", programName, message);
5986         }
5987     } else {
5988         if (appData.debugMode || appData.matchMode) {
5989             fprintf(stderr, "%s: %s: %s\n",
5990                     programName, message, strerror(error));
5991         }
5992         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
5993         message = buf;
5994     }
5995     ErrorPopUp(_("Error"), message, FALSE);
5996 }
5997
5998
5999 void DisplayMoveError(message)
6000      String message;
6001 {
6002     fromX = fromY = -1;
6003     ClearHighlights();
6004     DrawPosition(FALSE, NULL);
6005     if (appData.debugMode || appData.matchMode) {
6006         fprintf(stderr, "%s: %s\n", programName, message);
6007     }
6008     if (appData.popupMoveErrors) {
6009         ErrorPopUp(_("Error"), message, FALSE);
6010     } else {
6011         DisplayMessage(message, "");
6012     }
6013 }
6014
6015
6016 void DisplayFatalError(message, error, status)
6017      String message;
6018      int error, status;
6019 {
6020     char buf[MSG_SIZ];
6021
6022     errorExitStatus = status;
6023     if (error == 0) {
6024         fprintf(stderr, "%s: %s\n", programName, message);
6025     } else {
6026         fprintf(stderr, "%s: %s: %s\n",
6027                 programName, message, strerror(error));
6028         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6029         message = buf;
6030     }
6031     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6032       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6033     } else {
6034       ExitEvent(status);
6035     }
6036 }
6037
6038 void DisplayInformation(message)
6039      String message;
6040 {
6041     ErrorPopDown();
6042     ErrorPopUp(_("Information"), message, TRUE);
6043 }
6044
6045 void DisplayNote(message)
6046      String message;
6047 {
6048     ErrorPopDown();
6049     ErrorPopUp(_("Note"), message, FALSE);
6050 }
6051
6052 static int
6053 NullXErrorCheck(dpy, error_event)
6054      Display *dpy;
6055      XErrorEvent *error_event;
6056 {
6057     return 0;
6058 }
6059
6060 void DisplayIcsInteractionTitle(message)
6061      String message;
6062 {
6063   if (oldICSInteractionTitle == NULL) {
6064     /* Magic to find the old window title, adapted from vim */
6065     char *wina = getenv("WINDOWID");
6066     if (wina != NULL) {
6067       Window win = (Window) atoi(wina);
6068       Window root, parent, *children;
6069       unsigned int nchildren;
6070       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6071       for (;;) {
6072         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6073         if (!XQueryTree(xDisplay, win, &root, &parent,
6074                         &children, &nchildren)) break;
6075         if (children) XFree((void *)children);
6076         if (parent == root || parent == 0) break;
6077         win = parent;
6078       }
6079       XSetErrorHandler(oldHandler);
6080     }
6081     if (oldICSInteractionTitle == NULL) {
6082       oldICSInteractionTitle = "xterm";
6083     }
6084   }
6085   printf("\033]0;%s\007", message);
6086   fflush(stdout);
6087 }
6088
6089 char pendingReplyPrefix[MSG_SIZ];
6090 ProcRef pendingReplyPR;
6091
6092 void AskQuestionProc(w, event, prms, nprms)
6093      Widget w;
6094      XEvent *event;
6095      String *prms;
6096      Cardinal *nprms;
6097 {
6098     if (*nprms != 4) {
6099         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6100                 *nprms);
6101         return;
6102     }
6103     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6104 }
6105
6106 void AskQuestionPopDown()
6107 {
6108     if (!askQuestionUp) return;
6109     XtPopdown(askQuestionShell);
6110     XtDestroyWidget(askQuestionShell);
6111     askQuestionUp = False;
6112 }
6113
6114 void AskQuestionReplyAction(w, event, prms, nprms)
6115      Widget w;
6116      XEvent *event;
6117      String *prms;
6118      Cardinal *nprms;
6119 {
6120     char buf[MSG_SIZ];
6121     int err;
6122     String reply;
6123
6124     reply = XawDialogGetValueString(w = XtParent(w));
6125     strcpy(buf, pendingReplyPrefix);
6126     if (*buf) strcat(buf, " ");
6127     strcat(buf, reply);
6128     strcat(buf, "\n");
6129     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6130     AskQuestionPopDown();
6131
6132     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6133 }
6134
6135 void AskQuestionCallback(w, client_data, call_data)
6136      Widget w;
6137      XtPointer client_data, call_data;
6138 {
6139     String name;
6140     Arg args[16];
6141
6142     XtSetArg(args[0], XtNlabel, &name);
6143     XtGetValues(w, args, 1);
6144
6145     if (strcmp(name, _("cancel")) == 0) {
6146         AskQuestionPopDown();
6147     } else {
6148         AskQuestionReplyAction(w, NULL, NULL, NULL);
6149     }
6150 }
6151
6152 void AskQuestion(title, question, replyPrefix, pr)
6153      char *title, *question, *replyPrefix;
6154      ProcRef pr;
6155 {
6156     Arg args[16];
6157     Widget popup, layout, dialog, edit;
6158     Window root, child;
6159     int x, y, i;
6160     int win_x, win_y;
6161     unsigned int mask;
6162
6163     strcpy(pendingReplyPrefix, replyPrefix);
6164     pendingReplyPR = pr;
6165
6166     i = 0;
6167     XtSetArg(args[i], XtNresizable, True); i++;
6168     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6169     askQuestionShell = popup =
6170       XtCreatePopupShell(title, transientShellWidgetClass,
6171                          shellWidget, args, i);
6172
6173     layout =
6174       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6175                             layoutArgs, XtNumber(layoutArgs));
6176
6177     i = 0;
6178     XtSetArg(args[i], XtNlabel, question); i++;
6179     XtSetArg(args[i], XtNvalue, ""); i++;
6180     XtSetArg(args[i], XtNborderWidth, 0); i++;
6181     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
6182                                    layout, args, i);
6183
6184     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
6185                        (XtPointer) dialog);
6186     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
6187                        (XtPointer) dialog);
6188
6189     XtRealizeWidget(popup);
6190     CatchDeleteWindow(popup, "AskQuestionPopDown");
6191
6192     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
6193                   &x, &y, &win_x, &win_y, &mask);
6194
6195     XtSetArg(args[0], XtNx, x - 10);
6196     XtSetArg(args[1], XtNy, y - 30);
6197     XtSetValues(popup, args, 2);
6198
6199     XtPopup(popup, XtGrabExclusive);
6200     askQuestionUp = True;
6201
6202     edit = XtNameToWidget(dialog, "*value");
6203     XtSetKeyboardFocus(popup, edit);
6204 }
6205
6206
6207 void
6208 PlaySound(name)
6209      char *name;
6210 {
6211   if (*name == NULLCHAR) {
6212     return;
6213   } else if (strcmp(name, "$") == 0) {
6214     putc(BELLCHAR, stderr);
6215   } else {
6216     char buf[2048];
6217     snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
6218     system(buf);
6219   }
6220 }
6221
6222 void
6223 RingBell()
6224 {
6225   PlaySound(appData.soundMove);
6226 }
6227
6228 void
6229 PlayIcsWinSound()
6230 {
6231   PlaySound(appData.soundIcsWin);
6232 }
6233
6234 void
6235 PlayIcsLossSound()
6236 {
6237   PlaySound(appData.soundIcsLoss);
6238 }
6239
6240 void
6241 PlayIcsDrawSound()
6242 {
6243   PlaySound(appData.soundIcsDraw);
6244 }
6245
6246 void
6247 PlayIcsUnfinishedSound()
6248 {
6249   PlaySound(appData.soundIcsUnfinished);
6250 }
6251
6252 void
6253 PlayAlarmSound()
6254 {
6255   PlaySound(appData.soundIcsAlarm);
6256 }
6257
6258 void
6259 EchoOn()
6260 {
6261     system("stty echo");
6262 }
6263
6264 void
6265 EchoOff()
6266 {
6267     system("stty -echo");
6268 }
6269
6270 void
6271 Colorize(cc, continuation)
6272      ColorClass cc;
6273      int continuation;
6274 {
6275     char buf[MSG_SIZ];
6276     int count, outCount, error;
6277
6278     if (textColors[(int)cc].bg > 0) {
6279         if (textColors[(int)cc].fg > 0) {
6280             sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
6281                     textColors[(int)cc].fg, textColors[(int)cc].bg);
6282         } else {
6283             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
6284                     textColors[(int)cc].bg);
6285         }
6286     } else {
6287         if (textColors[(int)cc].fg > 0) {
6288             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
6289                     textColors[(int)cc].fg);
6290         } else {
6291             sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
6292         }
6293     }
6294     count = strlen(buf);
6295     outCount = OutputToProcess(NoProc, buf, count, &error);
6296     if (outCount < count) {
6297         DisplayFatalError(_("Error writing to display"), error, 1);
6298     }
6299
6300     if (continuation) return;
6301     switch (cc) {
6302     case ColorShout:
6303       PlaySound(appData.soundShout);
6304       break;
6305     case ColorSShout:
6306       PlaySound(appData.soundSShout);
6307       break;
6308     case ColorChannel1:
6309       PlaySound(appData.soundChannel1);
6310       break;
6311     case ColorChannel:
6312       PlaySound(appData.soundChannel);
6313       break;
6314     case ColorKibitz:
6315       PlaySound(appData.soundKibitz);
6316       break;
6317     case ColorTell:
6318       PlaySound(appData.soundTell);
6319       break;
6320     case ColorChallenge:
6321       PlaySound(appData.soundChallenge);
6322       break;
6323     case ColorRequest:
6324       PlaySound(appData.soundRequest);
6325       break;
6326     case ColorSeek:
6327       PlaySound(appData.soundSeek);
6328       break;
6329     case ColorNormal:
6330     case ColorNone:
6331     default:
6332       break;
6333     }
6334 }
6335
6336 char *UserName()
6337 {
6338     return getpwuid(getuid())->pw_name;
6339 }
6340
6341 static char *ExpandPathName(path)
6342      char *path;
6343 {
6344     static char static_buf[2000];
6345     char *d, *s, buf[2000];
6346     struct passwd *pwd;
6347
6348     s = path;
6349     d = static_buf;
6350
6351     while (*s && isspace(*s))
6352       ++s;
6353
6354     if (!*s) {
6355         *d = 0;
6356         return static_buf;
6357     }
6358
6359     if (*s == '~') {
6360         if (*(s+1) == '/') {
6361             strcpy(d, getpwuid(getuid())->pw_dir);
6362             strcat(d, s+1);
6363         }
6364         else {
6365             strcpy(buf, s+1);
6366             *strchr(buf, '/') = 0;
6367             pwd = getpwnam(buf);
6368             if (!pwd)
6369               {
6370                   fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
6371                           buf, path);
6372                   return NULL;
6373               }
6374             strcpy(d, pwd->pw_dir);
6375             strcat(d, strchr(s+1, '/'));
6376         }
6377     }
6378     else
6379       strcpy(d, s);
6380
6381     return static_buf;
6382 }
6383
6384 char *HostName()
6385 {
6386     static char host_name[MSG_SIZ];
6387
6388 #if HAVE_GETHOSTNAME
6389     gethostname(host_name, MSG_SIZ);
6390     return host_name;
6391 #else  /* not HAVE_GETHOSTNAME */
6392 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
6393     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
6394     return host_name;
6395 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
6396     return "localhost";
6397 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
6398 #endif /* not HAVE_GETHOSTNAME */
6399 }
6400
6401 guint delayedEventTimerTag = 0;
6402 DelayedEventCallback delayedEventCallback = 0;
6403
6404 void
6405 FireDelayedEvent(data)
6406      gpointer data;
6407 {
6408   /* remove timer */
6409   g_source_remove(delayedEventTimerTag);
6410   delayedEventTimerTag = 0;
6411
6412   /* call function */
6413   delayedEventCallback();
6414
6415   return;
6416 }
6417
6418 void
6419 ScheduleDelayedEvent(cb, millisec)
6420      DelayedEventCallback cb; guint millisec;
6421 {
6422     if(delayedEventTimerTag && delayedEventCallback == cb)
6423         // [HGM] alive: replace, rather than add or flush identical event
6424         g_source_remove(delayedEventTimerTag);
6425     delayedEventCallback = cb;
6426     delayedEventTimerTag = g_timeout_add(millisec,(GSourceFunc) FireDelayedEvent, NULL);
6427     return;
6428 }
6429
6430 DelayedEventCallback
6431 GetDelayedEvent()
6432 {
6433   if (delayedEventTimerTag)
6434     {
6435       return delayedEventCallback;
6436     }
6437   else
6438     {
6439       return NULL;
6440     }
6441 }
6442
6443 void
6444 CancelDelayedEvent()
6445 {
6446   if (delayedEventTimerTag)
6447     {
6448       g_source_remove(delayedEventTimerTag);
6449       delayedEventTimerTag = 0;
6450     }
6451
6452   return;
6453 }
6454
6455 guint loadGameTimerTag = 0;
6456
6457 int LoadGameTimerRunning()
6458 {
6459     return loadGameTimerTag != 0;
6460 }
6461
6462 int StopLoadGameTimer()
6463 {
6464     if (loadGameTimerTag != 0) {
6465         g_source_remove(loadGameTimerTag);
6466         loadGameTimerTag = 0;
6467         return TRUE;
6468     } else {
6469         return FALSE;
6470     }
6471 }
6472
6473 void
6474 LoadGameTimerCallback(data)
6475      gpointer data;
6476 {
6477   /* remove timer */
6478   g_source_remove(loadGameTimerTag);
6479   loadGameTimerTag = 0;
6480
6481   AutoPlayGameLoop();
6482   return;
6483 }
6484
6485 void
6486 StartLoadGameTimer(millisec)
6487      long millisec;
6488 {
6489   loadGameTimerTag =
6490     g_timeout_add( millisec, (GSourceFunc) LoadGameTimerCallback, NULL);
6491   return;
6492 }
6493
6494 guint analysisClockTag = 0;
6495
6496 gboolean
6497 AnalysisClockCallback(data)
6498      gpointer data;
6499 {
6500     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
6501          || appData.icsEngineAnalyze)
6502       {
6503         AnalysisPeriodicEvent(0);
6504         return 1; /* keep on going */
6505       }
6506     return 0; /* stop timer */
6507 }
6508
6509 void
6510 StartAnalysisClock()
6511 {
6512   analysisClockTag =
6513     g_timeout_add( 2000,(GSourceFunc) AnalysisClockCallback, NULL);
6514   return;
6515 }
6516
6517 guint clockTimerTag = 0;
6518
6519 int ClockTimerRunning()
6520 {
6521     return clockTimerTag != 0;
6522 }
6523
6524 int StopClockTimer()
6525 {
6526     if (clockTimerTag != 0)
6527       {
6528         g_source_remove(clockTimerTag);
6529         clockTimerTag = 0;
6530         return TRUE;
6531       }
6532     else
6533       {
6534         return FALSE;
6535       }
6536 }
6537
6538 void
6539 ClockTimerCallback(data)
6540      gpointer data;
6541 {
6542   /* remove timer */
6543   g_source_remove(clockTimerTag);
6544   clockTimerTag = 0;
6545
6546   DecrementClocks();
6547   return;
6548 }
6549
6550 void
6551 StartClockTimer(millisec)
6552      long millisec;
6553 {
6554   clockTimerTag = g_timeout_add(millisec,(GSourceFunc) ClockTimerCallback,NULL);
6555   return;
6556 }
6557
6558 void
6559 DisplayTimerLabel(w, color, timer, highlight)
6560      GtkWidget *w;
6561      char *color;
6562      long timer;
6563      int highlight;
6564 {
6565   gchar buf[MSG_SIZ];
6566
6567
6568   if (appData.clockMode) {
6569     sprintf(buf, "%s: %s", color, TimeString(timer));
6570   } else {
6571     sprintf(buf, "%s  ", color);
6572   }
6573   gtk_label_set_text(GTK_LABEL(w),buf);
6574
6575   /* check for low time warning */
6576 //    Pixel foregroundOrWarningColor = timerForegroundPixel;
6577
6578 //    if (timer > 0 &&
6579 //        appData.lowTimeWarning &&
6580 //        (timer / 1000) < appData.icsAlarmTime)
6581 //      foregroundOrWarningColor = lowTimeWarningColor;
6582 //
6583 //    if (appData.clockMode) {
6584 //      sprintf(buf, "%s: %s", color, TimeString(timer));
6585 //      XtSetArg(args[0], XtNlabel, buf);
6586 //    } else {
6587 //      sprintf(buf, "%s  ", color);
6588 //      XtSetArg(args[0], XtNlabel, buf);
6589 //    }
6590 //
6591 //    if (highlight) {
6592 //
6593 //      XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
6594 //      XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
6595 //    } else {
6596 //      XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
6597 //      XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
6598 //    }
6599 //
6600 //    XtSetValues(w, args, 3);
6601 //
6602 }
6603
6604 void
6605 DisplayWhiteClock(timeRemaining, highlight)
6606      long timeRemaining;
6607      int highlight;
6608 {
6609   if(appData.noGUI) return;
6610
6611   DisplayTimerLabel(GUI_Whiteclock, _("White"), timeRemaining, highlight);
6612   if (highlight && WindowIcon == BlackIcon)
6613     {
6614       WindowIcon = WhiteIcon;
6615       gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
6616     }
6617 }
6618
6619 void
6620 DisplayBlackClock(timeRemaining, highlight)
6621      long timeRemaining;
6622      int highlight;
6623 {
6624     if(appData.noGUI) return;
6625
6626     DisplayTimerLabel(GUI_Blackclock, _("Black"), timeRemaining, highlight);
6627     if (highlight && WindowIcon == WhiteIcon)
6628       {
6629         WindowIcon = BlackIcon;
6630         gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
6631       }
6632 }
6633
6634 #define CPNone 0
6635 #define CPReal 1
6636 #define CPComm 2
6637 #define CPSock 3
6638 #define CPLoop 4
6639 typedef int CPKind;
6640
6641 typedef struct {
6642     CPKind kind;
6643     int pid;
6644     int fdTo, fdFrom;
6645 } ChildProc;
6646
6647
6648 int StartChildProcess(cmdLine, dir, pr)
6649      char *cmdLine;
6650      char *dir;
6651      ProcRef *pr;
6652 {
6653     char *argv[64], *p;
6654     int i, pid;
6655     int to_prog[2], from_prog[2];
6656     ChildProc *cp;
6657     char buf[MSG_SIZ];
6658
6659     if (appData.debugMode) {
6660         fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
6661     }
6662
6663     /* We do NOT feed the cmdLine to the shell; we just
6664        parse it into blank-separated arguments in the
6665        most simple-minded way possible.
6666        */
6667     i = 0;
6668     strcpy(buf, cmdLine);
6669     p = buf;
6670     for (;;) {
6671         argv[i++] = p;
6672         p = strchr(p, ' ');
6673         if (p == NULL) break;
6674         *p++ = NULLCHAR;
6675     }
6676     argv[i] = NULL;
6677
6678     SetUpChildIO(to_prog, from_prog);
6679
6680     #ifdef SIGWINCH
6681     signal(SIGWINCH, TermSizeSigHandler);
6682     #endif
6683
6684     if ((pid = fork()) == 0) {
6685         /* Child process */
6686         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
6687         close(to_prog[1]);     // first close the unused pipe ends
6688         close(from_prog[0]);
6689         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
6690         dup2(from_prog[1], 1);
6691         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
6692         close(from_prog[1]);                   // and closing again loses one of the pipes!
6693         if(fileno(stderr) >= 2) // better safe than sorry...
6694                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
6695
6696         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
6697             perror(dir);
6698             exit(1);
6699         }
6700
6701         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
6702
6703         execvp(argv[0], argv);
6704
6705         /* If we get here, exec failed */
6706         perror(argv[0]);
6707         exit(1);
6708     }
6709
6710     /* Parent process */
6711     close(to_prog[0]);
6712     close(from_prog[1]);
6713
6714     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
6715     cp->kind = CPReal;
6716     cp->pid = pid;
6717     cp->fdFrom = from_prog[0];
6718     cp->fdTo = to_prog[1];
6719     *pr = (ProcRef) cp;
6720     return 0;
6721 }
6722
6723 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
6724 static RETSIGTYPE AlarmCallBack(int n)
6725 {
6726     return;
6727 }
6728
6729 void
6730 DestroyChildProcess(pr, signalType)
6731      ProcRef pr;
6732      int signalType;
6733 {
6734     ChildProc *cp = (ChildProc *) pr;
6735
6736     if (cp->kind != CPReal) return;
6737     cp->kind = CPNone;
6738     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
6739         signal(SIGALRM, AlarmCallBack);
6740         alarm(3);
6741         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
6742             kill(cp->pid, SIGKILL); // kill it forcefully
6743             wait((int *) 0);        // and wait again
6744         }
6745     } else {
6746         if (signalType) {
6747             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
6748         }
6749         /* Process is exiting either because of the kill or because of
6750            a quit command sent by the backend; either way, wait for it to die.
6751         */
6752         wait((int *) 0);
6753     }
6754     close(cp->fdFrom);
6755     close(cp->fdTo);
6756 }
6757
6758 void
6759 InterruptChildProcess(pr)
6760      ProcRef pr;
6761 {
6762     ChildProc *cp = (ChildProc *) pr;
6763
6764     if (cp->kind != CPReal) return;
6765     (void) kill(cp->pid, SIGINT); /* stop it thinking */
6766 }
6767
6768 int OpenTelnet(host, port, pr)
6769      char *host;
6770      char *port;
6771      ProcRef *pr;
6772 {
6773     char cmdLine[MSG_SIZ];
6774
6775     if (port[0] == NULLCHAR) {
6776       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
6777     } else {
6778       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
6779     }
6780     return StartChildProcess(cmdLine, "", pr);
6781 }
6782
6783 int OpenTCP(host, port, pr)
6784      char *host;
6785      char *port;
6786      ProcRef *pr;
6787 {
6788 #if OMIT_SOCKETS
6789     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
6790 #else  /* !OMIT_SOCKETS */
6791     int s;
6792     struct sockaddr_in sa;
6793     struct hostent     *hp;
6794     unsigned short uport;
6795     ChildProc *cp;
6796
6797     if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
6798         return errno;
6799     }
6800
6801     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
6802     sa.sin_family = AF_INET;
6803     sa.sin_addr.s_addr = INADDR_ANY;
6804     uport = (unsigned short) 0;
6805     sa.sin_port = htons(uport);
6806     if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
6807         return errno;
6808     }
6809
6810     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
6811     if (!(hp = gethostbyname(host))) {
6812         int b0, b1, b2, b3;
6813         if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
6814             hp = (struct hostent *) calloc(1, sizeof(struct hostent));
6815             hp->h_addrtype = AF_INET;
6816             hp->h_length = 4;
6817             hp->h_addr_list = (char **) calloc(2, sizeof(char *));
6818             hp->h_addr_list[0] = (char *) malloc(4);
6819             hp->h_addr_list[0][0] = b0;
6820             hp->h_addr_list[0][1] = b1;
6821             hp->h_addr_list[0][2] = b2;
6822             hp->h_addr_list[0][3] = b3;
6823         } else {
6824             return ENOENT;
6825         }
6826     }
6827     sa.sin_family = hp->h_addrtype;
6828     uport = (unsigned short) atoi(port);
6829     sa.sin_port = htons(uport);
6830     memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
6831
6832     if (connect(s, (struct sockaddr *) &sa,
6833                 sizeof(struct sockaddr_in)) < 0) {
6834         return errno;
6835     }
6836
6837     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
6838     cp->kind = CPSock;
6839     cp->pid = 0;
6840     cp->fdFrom = s;
6841     cp->fdTo = s;
6842     *pr = (ProcRef) cp;
6843
6844 #endif /* !OMIT_SOCKETS */
6845
6846     return 0;
6847 }
6848
6849 int OpenCommPort(name, pr)
6850      char *name;
6851      ProcRef *pr;
6852 {
6853     int fd;
6854     ChildProc *cp;
6855
6856     fd = open(name, 2, 0);
6857     if (fd < 0) return errno;
6858
6859     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
6860     cp->kind = CPComm;
6861     cp->pid = 0;
6862     cp->fdFrom = fd;
6863     cp->fdTo = fd;
6864     *pr = (ProcRef) cp;
6865
6866     return 0;
6867 }
6868
6869 int OpenLoopback(pr)
6870      ProcRef *pr;
6871 {
6872     ChildProc *cp;
6873     int to[2], from[2];
6874
6875     SetUpChildIO(to, from);
6876
6877     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
6878     cp->kind = CPLoop;
6879     cp->pid = 0;
6880     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
6881     cp->fdTo = to[1];
6882     *pr = (ProcRef) cp;
6883
6884     return 0;
6885 }
6886
6887 int OpenRcmd(host, user, cmd, pr)
6888      char *host, *user, *cmd;
6889      ProcRef *pr;
6890 {
6891     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
6892     return -1;
6893 }
6894
6895 #define INPUT_SOURCE_BUF_SIZE 8192
6896
6897 typedef struct {
6898     CPKind kind;
6899     int fd;
6900     int lineByLine;
6901     char *unused;
6902     InputCallback func;
6903     guint sid;
6904     char buf[INPUT_SOURCE_BUF_SIZE];
6905     VOIDSTAR closure;
6906 } InputSource;
6907
6908 void
6909 DoInputCallback(io,cond,data)
6910      GIOChannel   *io;
6911      GIOCondition  cond;
6912      gpointer *data;
6913 {
6914   /* read input from one of the input source (for example a chess program, ICS, etc).
6915    * and call a function that will handle the input
6916    */
6917
6918   int count; /* how many bytes did we read */
6919   int error; 
6920   char *p, *q;
6921   
6922   /* All information (callback function, file descriptor, etc) is
6923    * saved in an InputSource structure 
6924    */
6925   InputSource *is = (InputSource *) data; 
6926   
6927   if (is->lineByLine) 
6928     {
6929       count = read(is->fd, is->unused,
6930                    INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
6931
6932       if (count <= 0) 
6933         {
6934           (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
6935           return;
6936         }
6937       is->unused += count;
6938       p = is->buf;
6939       /* break input into lines and call the callback function on each
6940        * line 
6941        */
6942       while (p < is->unused) 
6943         {
6944           q = memchr(p, '\n', is->unused - p);
6945           if (q == NULL) break;
6946           q++;
6947           (is->func)(is, is->closure, p, q - p, 0);
6948           p = q;
6949         }
6950       /* remember not yet used part of the buffer */
6951       q = is->buf;
6952       while (p < is->unused) 
6953         {
6954           *q++ = *p++;
6955         }
6956       is->unused = q;
6957     }
6958   else 
6959     {
6960       /* read maximum length of input buffer and send the whole buffer
6961        * to the callback function 
6962        */
6963       count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
6964       if (count == -1)
6965         error = errno;
6966       else
6967         error = 0;
6968       (is->func)(is, is->closure, is->buf, count, error);
6969     }
6970   
6971   return;
6972 }
6973
6974 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
6975      ProcRef pr;
6976      int lineByLine;
6977      InputCallback func;
6978      VOIDSTAR closure;
6979 {
6980     InputSource *is;
6981     GIOChannel *channel;
6982     ChildProc *cp = (ChildProc *) pr;
6983
6984     is = (InputSource *) calloc(1, sizeof(InputSource));
6985     is->lineByLine = lineByLine;
6986     is->func = func;
6987     if (pr == NoProc) {
6988         is->kind = CPReal;
6989         is->fd = fileno(stdin);
6990     } else {
6991         is->kind = cp->kind;
6992         is->fd = cp->fdFrom;
6993     }
6994     if (lineByLine) 
6995       is->unused = is->buf;
6996     else
6997       is->unused = NULL;
6998
6999 //    is->xid = XtAppAddInput(appContext, is->fd,
7000 //                          (XtPointer) (XtInputReadMask),
7001 //                          (XtInputCallbackProc) DoInputCallback,
7002 //                          (XtPointer) is);
7003 //
7004
7005     /* TODO: will this work on windows?*/
7006     printf("DEBUG: fd=%d %d\n",is->fd,is);
7007
7008     channel = g_io_channel_unix_new(is->fd);
7009     g_io_channel_set_close_on_unref (channel, TRUE);
7010     is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
7011     is->closure = closure;
7012     return (InputSourceRef) is;
7013 }
7014
7015 void
7016 RemoveInputSource(isr)
7017      InputSourceRef isr;
7018 {
7019     InputSource *is = (InputSource *) isr;
7020
7021     if (is->sid == 0) return;
7022     g_source_remove(is->sid);
7023     is->sid = 0;
7024     return;
7025 }
7026
7027 int OutputToProcess(pr, message, count, outError)
7028      ProcRef pr;
7029      char *message;
7030      int count;
7031      int *outError;
7032 {
7033     ChildProc *cp = (ChildProc *) pr;
7034     int outCount;
7035
7036     if (pr == NoProc)
7037       outCount = fwrite(message, 1, count, stdout);
7038     else
7039       outCount = write(cp->fdTo, message, count);
7040
7041     if (outCount == -1)
7042       *outError = errno;
7043     else
7044       *outError = 0;
7045
7046     return outCount;
7047 }
7048
7049 /* Output message to process, with "ms" milliseconds of delay
7050    between each character. This is needed when sending the logon
7051    script to ICC, which for some reason doesn't like the
7052    instantaneous send. */
7053 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7054      ProcRef pr;
7055      char *message;
7056      int count;
7057      int *outError;
7058      long msdelay;
7059 {
7060     ChildProc *cp = (ChildProc *) pr;
7061     int outCount = 0;
7062     int r;
7063
7064     while (count--) {
7065         r = write(cp->fdTo, message++, 1);
7066         if (r == -1) {
7067             *outError = errno;
7068             return outCount;
7069         }
7070         ++outCount;
7071         if (msdelay >= 0)
7072           TimeDelay(msdelay);
7073     }
7074
7075     return outCount;
7076 }
7077
7078 /****   Animation code by Hugh Fisher, DCS, ANU.
7079
7080         Known problem: if a window overlapping the board is
7081         moved away while a piece is being animated underneath,
7082         the newly exposed area won't be updated properly.
7083         I can live with this.
7084
7085         Known problem: if you look carefully at the animation
7086         of pieces in mono mode, they are being drawn as solid
7087         shapes without interior detail while moving. Fixing
7088         this would be a major complication for minimal return.
7089 ****/
7090
7091 /*      Masks for XPM pieces. Black and white pieces can have
7092         different shapes, but in the interest of retaining my
7093         sanity pieces must have the same outline on both light
7094         and dark squares, and all pieces must use the same
7095         background square colors/images.                */
7096
7097 static int xpmDone = 0;
7098
7099 static void
7100 CreateAnimMasks (pieceDepth)
7101      int pieceDepth;
7102 {
7103   ChessSquare   piece;
7104   Pixmap        buf;
7105   GC            bufGC, maskGC;
7106   int           kind, n;
7107   unsigned long plane;
7108   XGCValues     values;
7109
7110   /* just return for gtk at the moment */
7111   return;
7112
7113   /* Need a bitmap just to get a GC with right depth */
7114   buf = XCreatePixmap(xDisplay, xBoardWindow,
7115                         8, 8, 1);
7116   values.foreground = 1;
7117   values.background = 0;
7118   /* Don't use XtGetGC, not read only */
7119   maskGC = XCreateGC(xDisplay, buf,
7120                     GCForeground | GCBackground, &values);
7121   XFreePixmap(xDisplay, buf);
7122
7123   buf = XCreatePixmap(xDisplay, xBoardWindow,
7124                       squareSize, squareSize, pieceDepth);
7125   values.foreground = XBlackPixel(xDisplay, xScreen);
7126   values.background = XWhitePixel(xDisplay, xScreen);
7127   bufGC = XCreateGC(xDisplay, buf,
7128                     GCForeground | GCBackground, &values);
7129
7130   for (piece = WhitePawn; piece <= BlackKing; piece++) {
7131     /* Begin with empty mask */
7132     if(!xpmDone) // [HGM] pieces: keep using existing
7133     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7134                                  squareSize, squareSize, 1);
7135     XSetFunction(xDisplay, maskGC, GXclear);
7136     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7137                    0, 0, squareSize, squareSize);
7138
7139     /* Take a copy of the piece */
7140     if (White(piece))
7141       kind = 0;
7142     else
7143       kind = 2;
7144     XSetFunction(xDisplay, bufGC, GXcopy);
7145     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7146               buf, bufGC,
7147               0, 0, squareSize, squareSize, 0, 0);
7148
7149     /* XOR the background (light) over the piece */
7150     XSetFunction(xDisplay, bufGC, GXxor);
7151     if (useImageSqs)
7152       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7153                 0, 0, squareSize, squareSize, 0, 0);
7154     else {
7155       XSetForeground(xDisplay, bufGC, lightSquareColor);
7156       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7157     }
7158
7159     /* We now have an inverted piece image with the background
7160        erased. Construct mask by just selecting all the non-zero
7161        pixels - no need to reconstruct the original image.      */
7162     XSetFunction(xDisplay, maskGC, GXor);
7163     plane = 1;
7164     /* Might be quicker to download an XImage and create bitmap
7165        data from it rather than this N copies per piece, but it
7166        only takes a fraction of a second and there is a much
7167        longer delay for loading the pieces.             */
7168     for (n = 0; n < pieceDepth; n ++) {
7169       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7170                  0, 0, squareSize, squareSize,
7171                  0, 0, plane);
7172       plane = plane << 1;
7173     }
7174   }
7175   /* Clean up */
7176   XFreePixmap(xDisplay, buf);
7177   XFreeGC(xDisplay, bufGC);
7178   XFreeGC(xDisplay, maskGC);
7179 }
7180
7181 static void
7182 InitAnimState (anim, info)
7183   AnimState * anim;
7184   XWindowAttributes * info;
7185 {
7186   XtGCMask  mask;
7187   XGCValues values;
7188
7189   /* Each buffer is square size, same depth as window */
7190 //  anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7191 //                      squareSize, squareSize, info->depth);
7192 //  anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7193 //                      squareSize, squareSize, info->depth);
7194 //
7195 //  /* Create a plain GC for blitting */
7196 //  mask = GCForeground | GCBackground | GCFunction |
7197 //         GCPlaneMask | GCGraphicsExposures;
7198 //  values.foreground = XBlackPixel(xDisplay, xScreen);
7199 //  values.background = XWhitePixel(xDisplay, xScreen);
7200 //  values.function   = GXcopy;
7201 //  values.plane_mask = AllPlanes;
7202 //  values.graphics_exposures = False;
7203 //  anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7204 //
7205 //  /* Piece will be copied from an existing context at
7206 //     the start of each new animation/drag. */
7207 //  anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7208 //
7209 //  /* Outline will be a read-only copy of an existing */
7210 //  anim->outlineGC = None;
7211 }
7212
7213 static void
7214 CreateAnimVars ()
7215 {
7216   static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
7217   XWindowAttributes info;
7218
7219   /* for gtk at the moment just ... */
7220   return;
7221
7222   if (xpmDone && gameInfo.variant == old) return;
7223   if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
7224   //  XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7225
7226   //  InitAnimState(&game, &info);
7227   //  InitAnimState(&player, &info);
7228
7229   /* For XPM pieces, we need bitmaps to use as masks. */
7230   //  if (useImages)
7231   //    CreateAnimMasks(info.depth);
7232    xpmDone = 1;
7233 }
7234
7235 #ifndef HAVE_USLEEP
7236
7237 static Boolean frameWaiting;
7238
7239 static RETSIGTYPE FrameAlarm (sig)
7240      int sig;
7241 {
7242   frameWaiting = False;
7243   /* In case System-V style signals.  Needed?? */
7244   signal(SIGALRM, FrameAlarm);
7245 }
7246
7247 static void
7248 FrameDelay (time)
7249      int time;
7250 {
7251   struct itimerval delay;
7252
7253   XSync(xDisplay, False);
7254
7255   if (time > 0) {
7256     frameWaiting = True;
7257     signal(SIGALRM, FrameAlarm);
7258     delay.it_interval.tv_sec =
7259       delay.it_value.tv_sec = time / 1000;
7260     delay.it_interval.tv_usec =
7261       delay.it_value.tv_usec = (time % 1000) * 1000;
7262     setitimer(ITIMER_REAL, &delay, NULL);
7263     while (frameWaiting) pause();
7264     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
7265     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
7266     setitimer(ITIMER_REAL, &delay, NULL);
7267   }
7268 }
7269
7270 #else
7271
7272 static void
7273 FrameDelay (time)
7274      int time;
7275 {
7276   //  XSync(xDisplay, False);
7277   if (time > 0)
7278     usleep(time * 1000);
7279 }
7280
7281 #endif
7282
7283 /*      Convert board position to corner of screen rect and color       */
7284
7285 static void
7286 ScreenSquare(column, row, pt, color)
7287      int column; int row; XPoint * pt; int * color;
7288 {
7289   if (flipView) {
7290     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
7291     pt->y = lineGap + row * (squareSize + lineGap);
7292   } else {
7293     pt->x = lineGap + column * (squareSize + lineGap);
7294     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
7295   }
7296   *color = SquareColor(row, column);
7297 }
7298
7299 /*      Convert window coords to square                 */
7300
7301 static void
7302 BoardSquare(x, y, column, row)
7303      int x; int y; int * column; int * row;
7304 {
7305   *column = EventToSquare(x, BOARD_WIDTH);
7306   if (flipView && *column >= 0)
7307     *column = BOARD_WIDTH - 1 - *column;
7308   *row = EventToSquare(y, BOARD_HEIGHT);
7309   if (!flipView && *row >= 0)
7310     *row = BOARD_HEIGHT - 1 - *row;
7311 }
7312
7313 /*   Utilities  */
7314
7315 #undef Max  /* just in case */
7316 #undef Min
7317 #define Max(a, b) ((a) > (b) ? (a) : (b))
7318 #define Min(a, b) ((a) < (b) ? (a) : (b))
7319
7320 static void
7321 SetRect(rect, x, y, width, height)
7322      XRectangle * rect; int x; int y; int width; int height;
7323 {
7324   rect->x = x;
7325   rect->y = y;
7326   rect->width  = width;
7327   rect->height = height;
7328 }
7329
7330 /*      Test if two frames overlap. If they do, return
7331         intersection rect within old and location of
7332         that rect within new. */
7333
7334 static Boolean
7335 Intersect(old, new, size, area, pt)
7336      XPoint * old; XPoint * new;
7337      int size; XRectangle * area; XPoint * pt;
7338 {
7339   if (old->x > new->x + size || new->x > old->x + size ||
7340       old->y > new->y + size || new->y > old->y + size) {
7341     return False;
7342   } else {
7343     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
7344             size - abs(old->x - new->x), size - abs(old->y - new->y));
7345     pt->x = Max(old->x - new->x, 0);
7346     pt->y = Max(old->y - new->y, 0);
7347     return True;
7348   }
7349 }
7350
7351 /*      For two overlapping frames, return the rect(s)
7352         in the old that do not intersect with the new.   */
7353
7354 static void
7355 CalcUpdateRects(old, new, size, update, nUpdates)
7356      XPoint * old; XPoint * new; int size;
7357      XRectangle update[]; int * nUpdates;
7358 {
7359   int        count;
7360
7361   /* If old = new (shouldn't happen) then nothing to draw */
7362   if (old->x == new->x && old->y == new->y) {
7363     *nUpdates = 0;
7364     return;
7365   }
7366   /* Work out what bits overlap. Since we know the rects
7367      are the same size we don't need a full intersect calc. */
7368   count = 0;
7369   /* Top or bottom edge? */
7370   if (new->y > old->y) {
7371     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
7372     count ++;
7373   } else if (old->y > new->y) {
7374     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
7375                               size, old->y - new->y);
7376     count ++;
7377   }
7378   /* Left or right edge - don't overlap any update calculated above. */
7379   if (new->x > old->x) {
7380     SetRect(&(update[count]), old->x, Max(new->y, old->y),
7381                               new->x - old->x, size - abs(new->y - old->y));
7382     count ++;
7383   } else if (old->x > new->x) {
7384     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
7385                               old->x - new->x, size - abs(new->y - old->y));
7386     count ++;
7387   }
7388   /* Done */
7389   *nUpdates = count;
7390 }
7391
7392 /*      Generate a series of frame coords from start->mid->finish.
7393         The movement rate doubles until the half way point is
7394         reached, then halves back down to the final destination,
7395         which gives a nice slow in/out effect. The algorithmn
7396         may seem to generate too many intermediates for short
7397         moves, but remember that the purpose is to attract the
7398         viewers attention to the piece about to be moved and
7399         then to where it ends up. Too few frames would be less
7400         noticeable.                                             */
7401
7402 static void
7403 Tween(start, mid, finish, factor, frames, nFrames)
7404      XPoint * start; XPoint * mid;
7405      XPoint * finish; int factor;
7406      XPoint frames[]; int * nFrames;
7407 {
7408   int fraction, n, count;
7409
7410   count = 0;
7411
7412   /* Slow in, stepping 1/16th, then 1/8th, ... */
7413   fraction = 1;
7414   for (n = 0; n < factor; n++)
7415     fraction *= 2;
7416   for (n = 0; n < factor; n++) {
7417     frames[count].x = start->x + (mid->x - start->x) / fraction;
7418     frames[count].y = start->y + (mid->y - start->y) / fraction;
7419     count ++;
7420     fraction = fraction / 2;
7421   }
7422
7423   /* Midpoint */
7424   frames[count] = *mid;
7425   count ++;
7426
7427   /* Slow out, stepping 1/2, then 1/4, ... */
7428   fraction = 2;
7429   for (n = 0; n < factor; n++) {
7430     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
7431     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
7432     count ++;
7433     fraction = fraction * 2;
7434   }
7435   *nFrames = count;
7436 }
7437
7438 /*      Draw a piece on the screen without disturbing what's there      */
7439
7440 static void
7441 SelectGCMask(piece, clip, outline, mask)
7442      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
7443 {
7444   GC source;
7445
7446   /* Bitmap for piece being moved. */
7447   if (appData.monoMode) {
7448       *mask = *pieceToSolid(piece);
7449   } else if (useImages) {
7450 #if HAVE_LIBXPM
7451       *mask = xpmMask[piece];
7452 #else
7453       *mask = ximMaskPm[piece];
7454 #endif
7455   } else {
7456       *mask = *pieceToSolid(piece);
7457   }
7458
7459   /* GC for piece being moved. Square color doesn't matter, but
7460      since it gets modified we make a copy of the original. */
7461   if (White(piece)) {
7462     if (appData.monoMode)
7463       source = bwPieceGC;
7464     else
7465       source = wlPieceGC;
7466   } else {
7467     if (appData.monoMode)
7468       source = wbPieceGC;
7469     else
7470       source = blPieceGC;
7471   }
7472   //  XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
7473
7474   /* Outline only used in mono mode and is not modified */
7475   if (White(piece))
7476     *outline = bwPieceGC;
7477   else
7478     *outline = wbPieceGC;
7479 }
7480
7481 static void
7482 OverlayPiece(piece, clip, outline,  dest)
7483      ChessSquare piece; GC clip; GC outline; Drawable dest;
7484 {
7485   int   kind;
7486
7487   if (!useImages) {
7488     /* Draw solid rectangle which will be clipped to shape of piece */
7489 //    XFillRectangle(xDisplay, dest, clip,
7490 //                 0, 0, squareSize, squareSize)
7491 ;
7492     if (appData.monoMode)
7493       /* Also draw outline in contrasting color for black
7494          on black / white on white cases                */
7495 //      XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
7496 //               0, 0, squareSize, squareSize, 0, 0, 1)
7497 ;
7498   } else {
7499     /* Copy the piece */
7500     if (White(piece))
7501       kind = 0;
7502     else
7503       kind = 2;
7504 //    XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
7505 //            dest, clip,
7506 //            0, 0, squareSize, squareSize,
7507 //            0, 0);
7508   }
7509 }
7510
7511 /* Animate the movement of a single piece */
7512
7513 static void
7514 BeginAnimation(anim, piece, startColor, start)
7515      AnimState *anim;
7516      ChessSquare piece;
7517      int startColor;
7518      XPoint * start;
7519 {
7520   Pixmap mask;
7521
7522   /* The old buffer is initialised with the start square (empty) */
7523   BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
7524   anim->prevFrame = *start;
7525
7526   /* The piece will be drawn using its own bitmap as a matte    */
7527 //  SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
7528 //  XSetClipMask(xDisplay, anim->pieceGC, mask);
7529 }
7530
7531 static void
7532 AnimationFrame(anim, frame, piece)
7533      AnimState *anim;
7534      XPoint *frame;
7535      ChessSquare piece;
7536 {
7537   XRectangle updates[4];
7538   XRectangle overlap;
7539   XPoint     pt;
7540   int        count, i;
7541
7542   /* Save what we are about to draw into the new buffer */
7543 //  XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
7544 //          frame->x, frame->y, squareSize, squareSize,
7545 //          0, 0);
7546
7547   /* Erase bits of the previous frame */
7548   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
7549     /* Where the new frame overlapped the previous,
7550        the contents in newBuf are wrong. */
7551 //    XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
7552 //            overlap.x, overlap.y,
7553 //            overlap.width, overlap.height,
7554 //            pt.x, pt.y);
7555     /* Repaint the areas in the old that don't overlap new */
7556     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
7557     for (i = 0; i < count; i++)
7558 //      XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
7559 //              updates[i].x - anim->prevFrame.x,
7560 //              updates[i].y - anim->prevFrame.y,
7561 //              updates[i].width, updates[i].height,
7562 //              updates[i].x, updates[i].y)
7563 ;
7564   } else {
7565     /* Easy when no overlap */
7566 //    XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
7567 //                0, 0, squareSize, squareSize,
7568 //                anim->prevFrame.x, anim->prevFrame.y);
7569   }
7570
7571   /* Save this frame for next time round */
7572 //  XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
7573 //              0, 0, squareSize, squareSize,
7574 //              0, 0);
7575   anim->prevFrame = *frame;
7576
7577   /* Draw piece over original screen contents, not current,
7578      and copy entire rect. Wipes out overlapping piece images. */
7579   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
7580 //  XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
7581 //              0, 0, squareSize, squareSize,
7582 //              frame->x, frame->y);
7583 }
7584
7585 static void
7586 EndAnimation (anim, finish)
7587      AnimState *anim;
7588      XPoint *finish;
7589 {
7590   XRectangle updates[4];
7591   XRectangle overlap;
7592   XPoint     pt;
7593   int        count, i;
7594
7595   /* The main code will redraw the final square, so we
7596      only need to erase the bits that don't overlap.    */
7597   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
7598     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
7599     for (i = 0; i < count; i++)
7600 //      XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
7601 //              updates[i].x - anim->prevFrame.x,
7602 //              updates[i].y - anim->prevFrame.y,
7603 //              updates[i].width, updates[i].height,
7604 //              updates[i].x, updates[i].y)
7605 ;
7606   } else {
7607 //    XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
7608 //              0, 0, squareSize, squareSize,
7609 //              anim->prevFrame.x, anim->prevFrame.y);
7610   }
7611 }
7612
7613 static void
7614 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
7615      AnimState *anim;
7616      ChessSquare piece; int startColor;
7617      XPoint * start; XPoint * finish;
7618      XPoint frames[]; int nFrames;
7619 {
7620   int n;
7621
7622   BeginAnimation(anim, piece, startColor, start);
7623   for (n = 0; n < nFrames; n++) {
7624     AnimationFrame(anim, &(frames[n]), piece);
7625     FrameDelay(appData.animSpeed);
7626   }
7627   EndAnimation(anim, finish);
7628 }
7629
7630 /* Main control logic for deciding what to animate and how */
7631
7632 void
7633 AnimateMove(board, fromX, fromY, toX, toY)
7634      Board board;
7635      int fromX;
7636      int fromY;
7637      int toX;
7638      int toY;
7639 {
7640   ChessSquare piece;
7641   int hop;
7642   XPoint      start, finish, mid;
7643   XPoint      frames[kFactor * 2 + 1];
7644   int         nFrames, startColor, endColor;
7645
7646   /* Are we animating? */
7647   if (!appData.animate || appData.blindfold)
7648     return;
7649
7650   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
7651      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
7652         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
7653
7654   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
7655   piece = board[fromY][fromX];
7656   if (piece >= EmptySquare) return;
7657
7658 #if DONT_HOP
7659   hop = FALSE;
7660 #else
7661   hop = (piece == WhiteKnight || piece == BlackKnight);
7662 #endif
7663
7664   if (appData.debugMode) {
7665       fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
7666                              _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
7667              piece, fromX, fromY, toX, toY);  }
7668
7669   ScreenSquare(fromX, fromY, &start, &startColor);
7670   ScreenSquare(toX, toY, &finish, &endColor);
7671
7672   if (hop) {
7673     /* Knight: make diagonal movement then straight */
7674     if (abs(toY - fromY) < abs(toX - fromX)) {
7675        mid.x = start.x + (finish.x - start.x) / 2;
7676        mid.y = finish.y;
7677      } else {
7678        mid.x = finish.x;
7679        mid.y = start.y + (finish.y - start.y) / 2;
7680      }
7681   } else {
7682     mid.x = start.x + (finish.x - start.x) / 2;
7683     mid.y = start.y + (finish.y - start.y) / 2;
7684   }
7685
7686   /* Don't use as many frames for very short moves */
7687   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
7688     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
7689   else
7690     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
7691   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
7692
7693   /* Be sure end square is redrawn */
7694   damage[toY][toX] = True;
7695 }
7696
7697 void
7698 DragPieceBegin(x, y)
7699      int x; int y;
7700 {
7701     int  boardX, boardY, color;
7702     XPoint corner;
7703
7704     /* Are we animating? */
7705     if (!appData.animateDragging || appData.blindfold)
7706       return;
7707
7708     /* Figure out which square we start in and the
7709        mouse position relative to top left corner. */
7710     BoardSquare(x, y, &boardX, &boardY);
7711     player.startBoardX = boardX;
7712     player.startBoardY = boardY;
7713     ScreenSquare(boardX, boardY, &corner, &color);
7714     player.startSquare  = corner;
7715     player.startColor   = color;
7716     /* As soon as we start dragging, the piece will jump slightly to
7717        be centered over the mouse pointer. */
7718     player.mouseDelta.x = squareSize/2;
7719     player.mouseDelta.y = squareSize/2;
7720     /* Initialise animation */
7721     player.dragPiece = PieceForSquare(boardX, boardY);
7722     /* Sanity check */
7723     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
7724         player.dragActive = True;
7725         BeginAnimation(&player, player.dragPiece, color, &corner);
7726         /* Mark this square as needing to be redrawn. Note that
7727            we don't remove the piece though, since logically (ie
7728            as seen by opponent) the move hasn't been made yet. */
7729            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
7730               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
7731 //           XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
7732 //                   corner.x, corner.y, squareSize, squareSize,
7733 //                   0, 0); // [HGM] zh: unstack in stead of grab
7734         damage[boardY][boardX] = True;
7735     } else {
7736         player.dragActive = False;
7737     }
7738 }
7739
7740 static void
7741 DragPieceMove(x, y)
7742      int x; int y;
7743 {
7744     XPoint corner;
7745
7746     /* Are we animating? */
7747     if (!appData.animateDragging || appData.blindfold)
7748       return;
7749
7750     /* Sanity check */
7751     if (! player.dragActive)
7752       return;
7753     /* Move piece, maintaining same relative position
7754        of mouse within square    */
7755     corner.x = x - player.mouseDelta.x;
7756     corner.y = y - player.mouseDelta.y;
7757     AnimationFrame(&player, &corner, player.dragPiece);
7758 #if HIGHDRAG
7759     if (appData.highlightDragging) {
7760         int boardX, boardY;
7761         BoardSquare(x, y, &boardX, &boardY);
7762         SetHighlights(fromX, fromY, boardX, boardY);
7763     }
7764 #endif
7765 }
7766
7767 void
7768 DragPieceEnd(x, y)
7769      int x; int y;
7770 {
7771     int boardX, boardY, color;
7772     XPoint corner;
7773
7774     /* Are we animating? */
7775     if (!appData.animateDragging || appData.blindfold)
7776       return;
7777
7778     /* Sanity check */
7779     if (! player.dragActive)
7780       return;
7781     /* Last frame in sequence is square piece is
7782        placed on, which may not match mouse exactly. */
7783     BoardSquare(x, y, &boardX, &boardY);
7784     ScreenSquare(boardX, boardY, &corner, &color);
7785     EndAnimation(&player, &corner);
7786
7787     /* Be sure end square is redrawn */
7788     damage[boardY][boardX] = True;
7789
7790     /* This prevents weird things happening with fast successive
7791        clicks which on my Sun at least can cause motion events
7792        without corresponding press/release. */
7793     player.dragActive = False;
7794 }
7795
7796 /* Handle expose event while piece being dragged */
7797
7798 static void
7799 DrawDragPiece ()
7800 {
7801   if (!player.dragActive || appData.blindfold)
7802     return;
7803
7804   /* What we're doing: logically, the move hasn't been made yet,
7805      so the piece is still in it's original square. But visually
7806      it's being dragged around the board. So we erase the square
7807      that the piece is on and draw it at the last known drag point. */
7808   BlankSquare(player.startSquare.x, player.startSquare.y,
7809                 player.startColor, EmptySquare, xBoardWindow);
7810   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
7811   damage[player.startBoardY][player.startBoardX] = TRUE;
7812 }
7813
7814 void
7815 SetProgramStats( FrontEndProgramStats * stats )
7816 {
7817   // [HR] TODO
7818   // [HGM] done, but perhaps backend should call this directly?
7819     EngineOutputUpdate( stats );
7820 }
7821
7822 #include <sys/ioctl.h>
7823 int get_term_width()
7824 {
7825     int fd, default_width;
7826
7827     fd = STDIN_FILENO;
7828     default_width = 79; // this is FICS default anyway...
7829
7830 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
7831     struct ttysize win;
7832     if (!ioctl(fd, TIOCGSIZE, &win))
7833         default_width = win.ts_cols;
7834 #elif defined(TIOCGWINSZ)
7835     struct winsize win;
7836     if (!ioctl(fd, TIOCGWINSZ, &win))
7837         default_width = win.ws_col;
7838 #endif
7839     return default_width;
7840 }
7841
7842 void update_ics_width()
7843 {
7844     static int old_width = 0;
7845     int new_width = get_term_width();
7846
7847     if (old_width != new_width)
7848        ics_printf("set width %d\n", new_width);
7849     old_width = new_width;
7850 }
7851
7852 void NotifyFrontendLogin()
7853 {
7854     update_ics_width();
7855 }