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