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