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