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