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