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