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