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