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