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