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