ee2ba94c300612fd779b85ae84222b4e27e669ab
[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;
5139
5140     if (w != boardWidget || errorExitStatus != -1) return;
5141
5142     if (event->type == ButtonPress) ErrorPopDown();
5143
5144     if (promotionUp) {
5145         if (event->type == ButtonPress) {
5146             XtPopdown(promotionShell);
5147             XtDestroyWidget(promotionShell);
5148             promotionUp = False;
5149             ClearHighlights();
5150             fromX = fromY = -1;
5151         } else {
5152             return;
5153         }
5154     }
5155
5156     x = EventToSquare(event->xbutton.x, BOARD_WIDTH);
5157     y = EventToSquare(event->xbutton.y, BOARD_HEIGHT);
5158     if (!flipView && y >= 0) {
5159         y = BOARD_HEIGHT - 1 - y;
5160     }
5161     if (flipView && x >= 0) {
5162         x = BOARD_WIDTH - 1 - x;
5163     }
5164
5165     /* [HGM] holdings: next 5 lines: ignore all clicks between board and holdings */
5166     if(event->type == ButtonPress
5167             && ( x == BOARD_LEFT-1 || x == BOARD_RGHT
5168               || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize
5169               || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize) )
5170         return;
5171
5172     if (fromX == -1) {
5173         if (event->type == ButtonPress) {
5174             /* First square */
5175             if (OKToStartUserMove(x, y)) {
5176                 fromX = x;
5177                 fromY = y;
5178                 second = 0;
5179                 DragPieceBegin(event->xbutton.x, event->xbutton.y);
5180                 if (appData.highlightDragging) {
5181                     SetHighlights(x, y, -1, -1);
5182                 }
5183             }
5184         }
5185         return;
5186     }
5187
5188     /* fromX != -1 */
5189     if (event->type == ButtonPress && gameMode != EditPosition &&
5190         x >= 0 && y >= 0) {
5191         ChessSquare fromP;
5192         ChessSquare toP;
5193         int frc;
5194
5195         /* Check if clicking again on the same color piece */
5196         fromP = boards[currentMove][fromY][fromX];
5197         toP = boards[currentMove][y][x];
5198         frc = gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom;
5199         if ((WhitePawn <= fromP && fromP <= WhiteKing && // [HGM] this test should go, as UserMoveTest now does it.
5200              WhitePawn <= toP && toP <= WhiteKing &&
5201              !(fromP == WhiteKing && toP == WhiteRook && frc)) ||   
5202             (BlackPawn <= fromP && fromP <= BlackKing && 
5203              BlackPawn <= toP && toP <= BlackKing &&
5204              !(fromP == BlackKing && toP == BlackRook && frc))) {
5205             /* Clicked again on same color piece -- changed his mind */
5206             second = (x == fromX && y == fromY);
5207             if (appData.highlightDragging) {
5208                 SetHighlights(x, y, -1, -1);
5209             } else {
5210                 ClearHighlights();
5211             }
5212             if (OKToStartUserMove(x, y)) {
5213                 fromX = x;
5214                 fromY = y;
5215                 DragPieceBegin(event->xbutton.x, event->xbutton.y);
5216             }
5217             return;
5218         }
5219     }
5220
5221     if (event->type == ButtonRelease && x == fromX && y == fromY) {
5222         DragPieceEnd(event->xbutton.x, event->xbutton.y);
5223         if (appData.animateDragging) {
5224             /* Undo animation damage if any */
5225             DrawPosition(FALSE, NULL);
5226         }
5227         if (second) {
5228             /* Second up/down in same square; just abort move */
5229             second = 0;
5230             fromX = fromY = -1;
5231             ClearHighlights();
5232             gotPremove = 0;
5233             ClearPremoveHighlights();
5234         } else {
5235             /* First upclick in same square; start click-click mode */
5236             SetHighlights(x, y, -1, -1);
5237         }
5238         return;
5239     }
5240
5241     /* Completed move */
5242     toX = x;
5243     toY = y;
5244     saveAnimate = appData.animate;
5245     if (event->type == ButtonPress) {
5246         /* Finish clickclick move */
5247         if (appData.animate || appData.highlightLastMove) {
5248             SetHighlights(fromX, fromY, toX, toY);
5249         } else {
5250             ClearHighlights();
5251         }
5252     } else {
5253         /* Finish drag move */
5254         if (appData.highlightLastMove) {
5255             SetHighlights(fromX, fromY, toX, toY);
5256         } else {
5257             ClearHighlights();
5258         }
5259         DragPieceEnd(event->xbutton.x, event->xbutton.y);
5260         /* Don't animate move and drag both */
5261         appData.animate = FALSE;
5262     }
5263     if (IsPromotion(fromX, fromY, toX, toY)) {
5264         if (appData.alwaysPromoteToQueen) {
5265             UserMoveEvent(fromX, fromY, toX, toY, 'q');
5266             if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5267             if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5268             fromX = fromY = -1;
5269         } else {
5270             SetHighlights(fromX, fromY, toX, toY);
5271             PromotionPopUp();
5272         }
5273     } else {
5274         UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
5275         if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5276         if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5277         fromX = fromY = -1;
5278     }
5279     appData.animate = saveAnimate;
5280     if (appData.animate || appData.animateDragging) {
5281         /* Undo animation damage if needed */
5282         DrawPosition(FALSE, NULL);
5283     }
5284 }
5285
5286 void AnimateUserMove (Widget w, XEvent * event,
5287                       String * params, Cardinal * nParams)
5288 {
5289     DragPieceMove(event->xmotion.x, event->xmotion.y);
5290 }
5291
5292 Widget CommentCreate(name, text, mutable, callback, lines)
5293      char *name, *text;
5294      int /*Boolean*/ mutable;
5295      XtCallbackProc callback;
5296      int lines;
5297 {
5298     Arg args[16];
5299     Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
5300     Dimension bw_width;
5301     int j;
5302
5303     j = 0;
5304     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
5305     XtGetValues(boardWidget, args, j);
5306
5307     j = 0;
5308     XtSetArg(args[j], XtNresizable, True);  j++;
5309 #if TOPLEVEL
5310     shell =
5311       XtCreatePopupShell(name, topLevelShellWidgetClass,
5312                          shellWidget, args, j);
5313 #else
5314     shell =
5315       XtCreatePopupShell(name, transientShellWidgetClass,
5316                          shellWidget, args, j);
5317 #endif
5318     layout =
5319       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
5320                             layoutArgs, XtNumber(layoutArgs));
5321     form =
5322       XtCreateManagedWidget("form", formWidgetClass, layout,
5323                             formArgs, XtNumber(formArgs));
5324
5325     j = 0;
5326     if (mutable) {
5327         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
5328         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
5329     }
5330     XtSetArg(args[j], XtNstring, text);  j++;
5331     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
5332     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
5333     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
5334     XtSetArg(args[j], XtNright, XtChainRight);  j++;
5335     XtSetArg(args[j], XtNresizable, True);  j++;
5336     XtSetArg(args[j], XtNwidth, bw_width);  j++; /*force wider than buttons*/
5337 #if 0
5338     XtSetArg(args[j], XtNscrollVertical, XawtextScrollWhenNeeded);  j++;
5339 #else
5340     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
5341     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
5342 #endif
5343     XtSetArg(args[j], XtNautoFill, True);  j++;
5344     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
5345     edit =
5346       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
5347
5348     if (mutable) {
5349         j = 0;
5350         XtSetArg(args[j], XtNfromVert, edit);  j++;
5351         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
5352         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
5353         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
5354         XtSetArg(args[j], XtNright, XtChainLeft); j++;
5355         b_ok =
5356           XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
5357         XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
5358
5359         j = 0;
5360         XtSetArg(args[j], XtNfromVert, edit);  j++;
5361         XtSetArg(args[j], XtNfromHoriz, b_ok);  j++;
5362         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
5363         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
5364         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
5365         XtSetArg(args[j], XtNright, XtChainLeft); j++;
5366         b_cancel =
5367           XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
5368         XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
5369
5370         j = 0;
5371         XtSetArg(args[j], XtNfromVert, edit);  j++;
5372         XtSetArg(args[j], XtNfromHoriz, b_cancel);  j++;
5373         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
5374         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
5375         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
5376         XtSetArg(args[j], XtNright, XtChainLeft); j++;
5377         b_clear =
5378           XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
5379         XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
5380     } else {
5381         j = 0;
5382         XtSetArg(args[j], XtNfromVert, edit);  j++;
5383         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
5384         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
5385         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
5386         XtSetArg(args[j], XtNright, XtChainLeft); j++;
5387         b_close =
5388           XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
5389         XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
5390
5391         j = 0;
5392         XtSetArg(args[j], XtNfromVert, edit);  j++;
5393         XtSetArg(args[j], XtNfromHoriz, b_close);  j++;
5394         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
5395         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
5396         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
5397         XtSetArg(args[j], XtNright, XtChainLeft); j++;
5398         b_edit =
5399           XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
5400         XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
5401     }
5402
5403     XtRealizeWidget(shell);
5404
5405     if (commentX == -1) {
5406         int xx, yy;
5407         Window junk;
5408         Dimension pw_height;
5409         Dimension ew_height;
5410
5411         j = 0;
5412         XtSetArg(args[j], XtNheight, &ew_height);  j++;
5413         XtGetValues(edit, args, j);
5414
5415         j = 0;
5416         XtSetArg(args[j], XtNheight, &pw_height);  j++;
5417         XtGetValues(shell, args, j);
5418         commentH = pw_height + (lines - 1) * ew_height;
5419         commentW = bw_width - 16;
5420
5421         XSync(xDisplay, False);
5422 #ifdef NOTDEF
5423         /* This code seems to tickle an X bug if it is executed too soon
5424            after xboard starts up.  The coordinates get transformed as if
5425            the main window was positioned at (0, 0).
5426            */
5427         XtTranslateCoords(shellWidget,
5428                           (bw_width - commentW) / 2, 0 - commentH / 2,
5429                           &commentX, &commentY);
5430 #else  /*!NOTDEF*/
5431         XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
5432                               RootWindowOfScreen(XtScreen(shellWidget)),
5433                               (bw_width - commentW) / 2, 0 - commentH / 2,
5434                               &xx, &yy, &junk);
5435         commentX = xx;
5436         commentY = yy;
5437 #endif /*!NOTDEF*/
5438         if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
5439     }
5440     j = 0;
5441     XtSetArg(args[j], XtNheight, commentH);  j++;
5442     XtSetArg(args[j], XtNwidth, commentW);  j++;
5443     XtSetArg(args[j], XtNx, commentX);  j++;
5444     XtSetArg(args[j], XtNy, commentY);  j++;
5445     XtSetValues(shell, args, j);
5446     XtSetKeyboardFocus(shell, edit);
5447
5448     return shell;
5449 }
5450
5451 /* Used for analysis window and ICS input window */
5452 Widget MiscCreate(name, text, mutable, callback, lines)
5453      char *name, *text;
5454      int /*Boolean*/ mutable;
5455      XtCallbackProc callback;
5456      int lines;
5457 {
5458     Arg args[16];
5459     Widget shell, layout, form, edit;
5460     Position x, y;
5461     Dimension bw_width, pw_height, ew_height, w, h;
5462     int j;
5463     int xx, yy;
5464     Window junk;
5465
5466     j = 0;
5467     XtSetArg(args[j], XtNresizable, True);  j++;
5468 #if TOPLEVEL
5469     shell =
5470       XtCreatePopupShell(name, topLevelShellWidgetClass,
5471                          shellWidget, args, j);
5472 #else
5473     shell =
5474       XtCreatePopupShell(name, transientShellWidgetClass,
5475                          shellWidget, args, j);
5476 #endif
5477     layout =
5478       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
5479                             layoutArgs, XtNumber(layoutArgs));
5480     form =
5481       XtCreateManagedWidget("form", formWidgetClass, layout,
5482                             formArgs, XtNumber(formArgs));
5483
5484     j = 0;
5485     if (mutable) {
5486         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
5487         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
5488     }
5489     XtSetArg(args[j], XtNstring, text);  j++;
5490     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
5491     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
5492     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
5493     XtSetArg(args[j], XtNright, XtChainRight);  j++;
5494     XtSetArg(args[j], XtNresizable, True);  j++;
5495 #if 0
5496     XtSetArg(args[j], XtNscrollVertical, XawtextScrollWhenNeeded);  j++;
5497 #else
5498     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
5499     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
5500 #endif
5501     XtSetArg(args[j], XtNautoFill, True);  j++;
5502     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
5503     edit =
5504       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
5505
5506     XtRealizeWidget(shell);
5507
5508     j = 0;
5509     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
5510     XtGetValues(boardWidget, args, j);
5511
5512     j = 0;
5513     XtSetArg(args[j], XtNheight, &ew_height);  j++;
5514     XtGetValues(edit, args, j);
5515
5516     j = 0;
5517     XtSetArg(args[j], XtNheight, &pw_height);  j++;
5518     XtGetValues(shell, args, j);
5519     h = pw_height + (lines - 1) * ew_height;
5520     w = bw_width - 16;
5521
5522     XSync(xDisplay, False);
5523 #ifdef NOTDEF
5524     /* This code seems to tickle an X bug if it is executed too soon
5525        after xboard starts up.  The coordinates get transformed as if
5526        the main window was positioned at (0, 0).
5527     */
5528     XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
5529 #else  /*!NOTDEF*/
5530     XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
5531                           RootWindowOfScreen(XtScreen(shellWidget)),
5532                           (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
5533 #endif /*!NOTDEF*/
5534     x = xx;
5535     y = yy;
5536     if (y < 0) y = 0; /*avoid positioning top offscreen*/
5537
5538     j = 0;
5539     XtSetArg(args[j], XtNheight, h);  j++;
5540     XtSetArg(args[j], XtNwidth, w);  j++;
5541     XtSetArg(args[j], XtNx, x);  j++;
5542     XtSetArg(args[j], XtNy, y);  j++;
5543     XtSetValues(shell, args, j);
5544
5545     return shell;
5546 }
5547
5548
5549 static int savedIndex;  /* gross that this is global */
5550
5551 void EditCommentPopUp(index, title, text)
5552      int index;
5553      char *title, *text;
5554 {
5555     Widget edit;
5556     Arg args[16];
5557     int j;
5558
5559     savedIndex = index;
5560     if (text == NULL) text = "";
5561
5562     if (editShell == NULL) {
5563         editShell =
5564           CommentCreate(title, text, True, EditCommentCallback, 4);
5565         XtRealizeWidget(editShell);
5566         CatchDeleteWindow(editShell, "EditCommentPopDown");
5567     } else {
5568         edit = XtNameToWidget(editShell, "*form.text");
5569         j = 0;
5570         XtSetArg(args[j], XtNstring, text); j++;
5571         XtSetValues(edit, args, j);
5572         j = 0;
5573         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
5574         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
5575         XtSetValues(editShell, args, j);
5576     }
5577
5578     XtPopup(editShell, XtGrabNone);
5579
5580     editUp = True;
5581     j = 0;
5582     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
5583     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
5584                 args, j);
5585 }
5586
5587 void EditCommentCallback(w, client_data, call_data)
5588      Widget w;
5589      XtPointer client_data, call_data;
5590 {
5591     String name, val;
5592     Arg args[16];
5593     int j;
5594     Widget edit;
5595
5596     j = 0;
5597     XtSetArg(args[j], XtNlabel, &name);  j++;
5598     XtGetValues(w, args, j);
5599
5600     if (strcmp(name, _("ok")) == 0) {
5601         edit = XtNameToWidget(editShell, "*form.text");
5602         j = 0;
5603         XtSetArg(args[j], XtNstring, &val); j++;
5604         XtGetValues(edit, args, j);
5605         ReplaceComment(savedIndex, val);
5606         EditCommentPopDown();
5607     } else if (strcmp(name, _("cancel")) == 0) {
5608         EditCommentPopDown();
5609     } else if (strcmp(name, _("clear")) == 0) {
5610         edit = XtNameToWidget(editShell, "*form.text");
5611         XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5612         XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5613     }
5614 }
5615
5616 void EditCommentPopDown()
5617 {
5618     Arg args[16];
5619     int j;
5620
5621     if (!editUp) return;
5622     j = 0;
5623     XtSetArg(args[j], XtNx, &commentX); j++;
5624     XtSetArg(args[j], XtNy, &commentY); j++;
5625     XtSetArg(args[j], XtNheight, &commentH); j++;
5626     XtSetArg(args[j], XtNwidth, &commentW); j++;
5627     XtGetValues(editShell, args, j);
5628     XtPopdown(editShell);
5629     editUp = False;
5630     j = 0;
5631     XtSetArg(args[j], XtNleftBitmap, None); j++;
5632     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
5633                 args, j);
5634 }
5635
5636 void ICSInputBoxPopUp()
5637 {
5638     Widget edit;
5639     Arg args[16];
5640     int j;
5641     char *title = _("ICS Input");
5642     XtTranslations tr;
5643
5644     if (ICSInputShell == NULL) {
5645         ICSInputShell = MiscCreate(title, "", True, NULL, 1);
5646         tr = XtParseTranslationTable(ICSInputTranslations);
5647         edit = XtNameToWidget(ICSInputShell, "*form.text");
5648         XtOverrideTranslations(edit, tr);
5649         XtRealizeWidget(ICSInputShell);
5650         CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
5651
5652     } else {
5653         edit = XtNameToWidget(ICSInputShell, "*form.text");
5654         j = 0;
5655         XtSetArg(args[j], XtNstring, ""); j++;
5656         XtSetValues(edit, args, j);
5657         j = 0;
5658         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
5659         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
5660         XtSetValues(ICSInputShell, args, j);
5661     }
5662
5663     XtPopup(ICSInputShell, XtGrabNone);
5664     XtSetKeyboardFocus(ICSInputShell, edit);
5665
5666     ICSInputBoxUp = True;
5667     j = 0;
5668     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
5669     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
5670                 args, j);
5671 }
5672
5673 void ICSInputSendText()
5674 {
5675     Widget edit;
5676     int j;
5677     Arg args[16];
5678     String val;
5679
5680     edit = XtNameToWidget(ICSInputShell, "*form.text");
5681     j = 0;
5682     XtSetArg(args[j], XtNstring, &val); j++;
5683     XtGetValues(edit, args, j);
5684     SendMultiLineToICS(val);
5685     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5686     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5687 }
5688
5689 void ICSInputBoxPopDown()
5690 {
5691     Arg args[16];
5692     int j;
5693
5694     if (!ICSInputBoxUp) return;
5695     j = 0;
5696     XtPopdown(ICSInputShell);
5697     ICSInputBoxUp = False;
5698     j = 0;
5699     XtSetArg(args[j], XtNleftBitmap, None); j++;
5700     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
5701                 args, j);
5702 }
5703
5704 void CommentPopUp(title, text)
5705      char *title, *text;
5706 {
5707     Arg args[16];
5708     int j;
5709     Widget edit;
5710
5711     if (commentShell == NULL) {
5712         commentShell =
5713           CommentCreate(title, text, False, CommentCallback, 4);
5714         XtRealizeWidget(commentShell);
5715         CatchDeleteWindow(commentShell, "CommentPopDown");
5716     } else {
5717         edit = XtNameToWidget(commentShell, "*form.text");
5718         j = 0;
5719         XtSetArg(args[j], XtNstring, text); j++;
5720         XtSetValues(edit, args, j);
5721         j = 0;
5722         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
5723         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
5724         XtSetValues(commentShell, args, j);
5725     }
5726
5727     XtPopup(commentShell, XtGrabNone);
5728     XSync(xDisplay, False);
5729
5730     commentUp = True;
5731 }
5732
5733 void AnalysisPopUp(title, text)
5734      char *title, *text;
5735 {
5736     Arg args[16];
5737     int j;
5738     Widget edit;
5739
5740     if (analysisShell == NULL) {
5741         analysisShell = MiscCreate(title, text, False, NULL, 4);
5742         XtRealizeWidget(analysisShell);
5743         CatchDeleteWindow(analysisShell, "AnalysisPopDown");
5744
5745     } else {
5746         edit = XtNameToWidget(analysisShell, "*form.text");
5747         j = 0;
5748         XtSetArg(args[j], XtNstring, text); j++;
5749         XtSetValues(edit, args, j);
5750         j = 0;
5751         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
5752         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
5753         XtSetValues(analysisShell, args, j);
5754     }
5755
5756     if (!analysisUp) {
5757         XtPopup(analysisShell, XtGrabNone);
5758     }
5759     XSync(xDisplay, False);
5760
5761     analysisUp = True;
5762 }
5763
5764 void CommentCallback(w, client_data, call_data)
5765      Widget w;
5766      XtPointer client_data, call_data;
5767 {
5768     String name;
5769     Arg args[16];
5770     int j;
5771
5772     j = 0;
5773     XtSetArg(args[j], XtNlabel, &name);  j++;
5774     XtGetValues(w, args, j);
5775
5776     if (strcmp(name, _("close")) == 0) {
5777         CommentPopDown();
5778     } else if (strcmp(name, _("edit")) == 0) {
5779         CommentPopDown();
5780         EditCommentEvent();
5781     }
5782 }
5783
5784
5785 void CommentPopDown()
5786 {
5787     Arg args[16];
5788     int j;
5789
5790     if (!commentUp) return;
5791     j = 0;
5792     XtSetArg(args[j], XtNx, &commentX); j++;
5793     XtSetArg(args[j], XtNy, &commentY); j++;
5794     XtSetArg(args[j], XtNwidth, &commentW); j++;
5795     XtSetArg(args[j], XtNheight, &commentH); j++;
5796     XtGetValues(commentShell, args, j);
5797     XtPopdown(commentShell);
5798     XSync(xDisplay, False);
5799     commentUp = False;
5800 }
5801
5802 void AnalysisPopDown()
5803 {
5804     if (!analysisUp) return;
5805     XtPopdown(analysisShell);
5806     XSync(xDisplay, False);
5807     analysisUp = False;
5808     if (appData.icsEngineAnalyze) ExitAnalyzeMode();    /* [DM] icsEngineAnalyze */
5809 }
5810
5811
5812 void FileNamePopUp(label, def, proc, openMode)
5813      char *label;
5814      char *def;
5815      FileProc proc;
5816      char *openMode;
5817 {
5818     Arg args[16];
5819     Widget popup, layout, dialog, edit;
5820     Window root, child;
5821     int x, y, i;
5822     int win_x, win_y;
5823     unsigned int mask;
5824
5825     fileProc = proc;            /* I can't see a way not */
5826     fileOpenMode = openMode;    /*   to use globals here */
5827
5828     i = 0;
5829     XtSetArg(args[i], XtNresizable, True); i++;
5830     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
5831     XtSetArg(args[i], XtNtitle, XtNewString(_("File name prompt"))); i++;
5832     fileNameShell = popup =
5833       XtCreatePopupShell("File name prompt", transientShellWidgetClass,
5834                          shellWidget, args, i);
5835
5836     layout =
5837       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
5838                             layoutArgs, XtNumber(layoutArgs));
5839
5840     i = 0;
5841     XtSetArg(args[i], XtNlabel, label); i++;
5842     XtSetArg(args[i], XtNvalue, def); i++;
5843     XtSetArg(args[i], XtNborderWidth, 0); i++;
5844     dialog = XtCreateManagedWidget("fileName", dialogWidgetClass,
5845                                    layout, args, i);
5846
5847     XawDialogAddButton(dialog, _("ok"), FileNameCallback, (XtPointer) dialog);
5848     XawDialogAddButton(dialog, _("cancel"), FileNameCallback,
5849                        (XtPointer) dialog);
5850
5851     XtRealizeWidget(popup);
5852     CatchDeleteWindow(popup, "FileNamePopDown");
5853
5854     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
5855                   &x, &y, &win_x, &win_y, &mask);
5856
5857     XtSetArg(args[0], XtNx, x - 10);
5858     XtSetArg(args[1], XtNy, y - 30);
5859     XtSetValues(popup, args, 2);
5860
5861     XtPopup(popup, XtGrabExclusive);
5862     filenameUp = True;
5863
5864     edit = XtNameToWidget(dialog, "*value");
5865     XtSetKeyboardFocus(popup, edit);
5866 }
5867
5868 void FileNamePopDown()
5869 {
5870     if (!filenameUp) return;
5871     XtPopdown(fileNameShell);
5872     XtDestroyWidget(fileNameShell);
5873     filenameUp = False;
5874     ModeHighlight();
5875 }
5876
5877 void FileNameCallback(w, client_data, call_data)
5878      Widget w;
5879      XtPointer client_data, call_data;
5880 {
5881     String name;
5882     Arg args[16];
5883
5884     XtSetArg(args[0], XtNlabel, &name);
5885     XtGetValues(w, args, 1);
5886
5887     if (strcmp(name, _("cancel")) == 0) {
5888         FileNamePopDown();
5889         return;
5890     }
5891
5892     FileNameAction(w, NULL, NULL, NULL);
5893 }
5894
5895 void FileNameAction(w, event, prms, nprms)
5896      Widget w;
5897      XEvent *event;
5898      String *prms;
5899      Cardinal *nprms;
5900 {
5901     char buf[MSG_SIZ];
5902     String name;
5903     FILE *f;
5904     char *p, *fullname;
5905     int index;
5906
5907     name = XawDialogGetValueString(w = XtParent(w));
5908
5909     if ((name != NULL) && (*name != NULLCHAR)) {
5910         strcpy(buf, name);
5911         XtPopdown(w = XtParent(XtParent(w)));
5912         XtDestroyWidget(w);
5913         filenameUp = False;
5914
5915         p = strrchr(buf, ' ');
5916         if (p == NULL) {
5917             index = 0;
5918         } else {
5919             *p++ = NULLCHAR;
5920             index = atoi(p);
5921         }
5922         fullname = ExpandPathName(buf);
5923         if (!fullname) {
5924             ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5925         }
5926         else {
5927             f = fopen(fullname, fileOpenMode);
5928             if (f == NULL) {
5929                 DisplayError(_("Failed to open file"), errno);
5930             } else {
5931                 (void) (*fileProc)(f, index, buf);
5932             }
5933         }
5934         ModeHighlight();
5935         return;
5936     }
5937
5938     XtPopdown(w = XtParent(XtParent(w)));
5939     XtDestroyWidget(w);
5940     filenameUp = False;
5941     ModeHighlight();
5942 }
5943
5944 void PromotionPopUp()
5945 {
5946     Arg args[16];
5947     Widget dialog, layout;
5948     Position x, y;
5949     Dimension bw_width, pw_width;
5950     int j;
5951
5952     j = 0;
5953     XtSetArg(args[j], XtNwidth, &bw_width); j++;
5954     XtGetValues(boardWidget, args, j);
5955
5956     j = 0;
5957     XtSetArg(args[j], XtNresizable, True); j++;
5958     XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5959     promotionShell =
5960       XtCreatePopupShell("Promotion", transientShellWidgetClass,
5961                          shellWidget, args, j);
5962     layout =
5963       XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5964                             layoutArgs, XtNumber(layoutArgs));
5965
5966     j = 0;
5967     XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5968     XtSetArg(args[j], XtNborderWidth, 0); j++;
5969     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5970                                    layout, args, j);
5971
5972   if(gameInfo.variant != VariantShogi) {
5973     XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5974                        (XtPointer) dialog);
5975     XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5976                        (XtPointer) dialog);
5977     XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5978                        (XtPointer) dialog);
5979     XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5980                        (XtPointer) dialog);
5981     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5982         gameInfo.variant == VariantGiveaway) {
5983       XawDialogAddButton(dialog, _("King"), PromotionCallback,
5984                          (XtPointer) dialog);
5985     }
5986     if(gameInfo.variant == VariantCapablanca || 
5987        gameInfo.variant == VariantGothic || 
5988        gameInfo.variant == VariantCapaRandom) {
5989       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5990                          (XtPointer) dialog);
5991       XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5992                          (XtPointer) dialog);
5993     }
5994   } else // [HGM] shogi
5995   {
5996       XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5997                          (XtPointer) dialog);
5998       XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5999                          (XtPointer) dialog);
6000   }
6001     XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
6002                        (XtPointer) dialog);
6003
6004     XtRealizeWidget(promotionShell);
6005     CatchDeleteWindow(promotionShell, "PromotionPopDown");
6006
6007     j = 0;
6008     XtSetArg(args[j], XtNwidth, &pw_width); j++;
6009     XtGetValues(promotionShell, args, j);
6010
6011     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
6012                       lineGap + squareSize/3 +
6013                       ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
6014                        0 : 6*(squareSize + lineGap)), &x, &y);
6015
6016     j = 0;
6017     XtSetArg(args[j], XtNx, x); j++;
6018     XtSetArg(args[j], XtNy, y); j++;
6019     XtSetValues(promotionShell, args, j);
6020
6021     XtPopup(promotionShell, XtGrabNone);
6022
6023     promotionUp = True;
6024 }
6025
6026 void PromotionPopDown()
6027 {
6028     if (!promotionUp) return;
6029     XtPopdown(promotionShell);
6030     XtDestroyWidget(promotionShell);
6031     promotionUp = False;
6032 }
6033
6034 void PromotionCallback(w, client_data, call_data)
6035      Widget w;
6036      XtPointer client_data, call_data;
6037 {
6038     String name;
6039     Arg args[16];
6040     int promoChar;
6041
6042     XtSetArg(args[0], XtNlabel, &name);
6043     XtGetValues(w, args, 1);
6044
6045     PromotionPopDown();
6046
6047     if (fromX == -1) return;
6048
6049     if (strcmp(name, _("cancel")) == 0) {
6050         fromX = fromY = -1;
6051         ClearHighlights();
6052         return;
6053     } else if (strcmp(name, _("Knight")) == 0) {
6054         promoChar = 'n';
6055     } else if (strcmp(name, _("Promote")) == 0) {
6056         promoChar = '+';
6057     } else if (strcmp(name, _("Defer")) == 0) {
6058         promoChar = '=';
6059     } else {
6060         promoChar = ToLower(name[0]);
6061     }
6062
6063     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
6064
6065     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
6066     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
6067     fromX = fromY = -1;
6068 }
6069
6070
6071 void ErrorCallback(w, client_data, call_data)
6072      Widget w;
6073      XtPointer client_data, call_data;
6074 {
6075     errorUp = False;
6076     XtPopdown(w = XtParent(XtParent(XtParent(w))));
6077     XtDestroyWidget(w);
6078     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
6079 }
6080
6081
6082 void ErrorPopDown()
6083 {
6084     if (!errorUp) return;
6085     errorUp = False;
6086     XtPopdown(errorShell);
6087     XtDestroyWidget(errorShell);
6088     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
6089 }
6090
6091 void ErrorPopUp(title, label, modal)
6092      char *title, *label;
6093      int modal;
6094 {
6095     Arg args[16];
6096     Widget dialog, layout;
6097     Position x, y;
6098     int xx, yy;
6099     Window junk;
6100     Dimension bw_width, pw_width;
6101     Dimension pw_height;
6102     int i;
6103
6104     i = 0;
6105     XtSetArg(args[i], XtNresizable, True);  i++;
6106     XtSetArg(args[i], XtNtitle, title); i++;
6107     errorShell =
6108       XtCreatePopupShell("errorpopup", transientShellWidgetClass,
6109                          shellWidget, args, i);
6110     layout =
6111       XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
6112                             layoutArgs, XtNumber(layoutArgs));
6113
6114     i = 0;
6115     XtSetArg(args[i], XtNlabel, label); i++;
6116     XtSetArg(args[i], XtNborderWidth, 0); i++;
6117     dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
6118                                    layout, args, i);
6119
6120     XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
6121
6122     XtRealizeWidget(errorShell);
6123     CatchDeleteWindow(errorShell, "ErrorPopDown");
6124
6125     i = 0;
6126     XtSetArg(args[i], XtNwidth, &bw_width);  i++;
6127     XtGetValues(boardWidget, args, i);
6128     i = 0;
6129     XtSetArg(args[i], XtNwidth, &pw_width);  i++;
6130     XtSetArg(args[i], XtNheight, &pw_height);  i++;
6131     XtGetValues(errorShell, args, i);
6132
6133 #ifdef NOTDEF
6134     /* This code seems to tickle an X bug if it is executed too soon
6135        after xboard starts up.  The coordinates get transformed as if
6136        the main window was positioned at (0, 0).
6137        */
6138     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
6139                       0 - pw_height + squareSize / 3, &x, &y);
6140 #else
6141     XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
6142                           RootWindowOfScreen(XtScreen(boardWidget)),
6143                           (bw_width - pw_width) / 2,
6144                           0 - pw_height + squareSize / 3, &xx, &yy, &junk);
6145     x = xx;
6146     y = yy;
6147 #endif
6148     if (y < 0) y = 0; /*avoid positioning top offscreen*/
6149
6150     i = 0;
6151     XtSetArg(args[i], XtNx, x);  i++;
6152     XtSetArg(args[i], XtNy, y);  i++;
6153     XtSetValues(errorShell, args, i);
6154
6155     errorUp = True;
6156     XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
6157 }
6158
6159 /* Disable all user input other than deleting the window */
6160 static int frozen = 0;
6161 void FreezeUI()
6162 {
6163   if (frozen) return;
6164   /* Grab by a widget that doesn't accept input */
6165   XtAddGrab(messageWidget, TRUE, FALSE);
6166   frozen = 1;
6167 }
6168
6169 /* Undo a FreezeUI */
6170 void ThawUI()
6171 {
6172   if (!frozen) return;
6173   XtRemoveGrab(messageWidget);
6174   frozen = 0;
6175 }
6176
6177 char *ModeToWidgetName(mode)
6178      GameMode mode;
6179 {
6180     switch (mode) {
6181       case BeginningOfGame:
6182         if (appData.icsActive)
6183           return "menuMode.ICS Client";
6184         else if (appData.noChessProgram ||
6185                  *appData.cmailGameName != NULLCHAR)
6186           return "menuMode.Edit Game";
6187         else
6188           return "menuMode.Machine Black";
6189       case MachinePlaysBlack:
6190         return "menuMode.Machine Black";
6191       case MachinePlaysWhite:
6192         return "menuMode.Machine White";
6193       case AnalyzeMode:
6194         return "menuMode.Analysis Mode";
6195       case AnalyzeFile:
6196         return "menuMode.Analyze File";
6197       case TwoMachinesPlay:
6198         return "menuMode.Two Machines";
6199       case EditGame:
6200         return "menuMode.Edit Game";
6201       case PlayFromGameFile:
6202         return "menuFile.Load Game";
6203       case EditPosition:
6204         return "menuMode.Edit Position";
6205       case Training:
6206         return "menuMode.Training";
6207       case IcsPlayingWhite:
6208       case IcsPlayingBlack:
6209       case IcsObserving:
6210       case IcsIdle:
6211       case IcsExamining:
6212         return "menuMode.ICS Client";
6213       default:
6214       case EndOfGame:
6215         return NULL;
6216     }
6217 }
6218
6219 void ModeHighlight()
6220 {
6221     Arg args[16];
6222     static int oldPausing = FALSE;
6223     static GameMode oldmode = (GameMode) -1;
6224     char *wname;
6225
6226     if (!boardWidget || !XtIsRealized(boardWidget)) return;
6227
6228     if (pausing != oldPausing) {
6229         oldPausing = pausing;
6230         if (pausing) {
6231             XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6232         } else {
6233             XtSetArg(args[0], XtNleftBitmap, None);
6234         }
6235         XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
6236                     args, 1);
6237
6238         if (appData.showButtonBar) {
6239 #if 0
6240           if (pausing) {
6241             XtSetArg(args[0], XtNbackground, buttonForegroundPixel);
6242             XtSetArg(args[1], XtNforeground, buttonBackgroundPixel);
6243           } else {
6244             XtSetArg(args[0], XtNbackground, buttonBackgroundPixel);
6245             XtSetArg(args[1], XtNforeground, buttonForegroundPixel);
6246           }
6247 #else
6248           /* Always toggle, don't set.  Previous code messes up when
6249              invoked while the button is pressed, as releasing it
6250              toggles the state again. */
6251           {
6252             Pixel oldbg, oldfg;
6253             XtSetArg(args[0], XtNbackground, &oldbg);
6254             XtSetArg(args[1], XtNforeground, &oldfg);
6255             XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
6256                         args, 2);
6257             XtSetArg(args[0], XtNbackground, oldfg);
6258             XtSetArg(args[1], XtNforeground, oldbg);
6259           }
6260 #endif
6261           XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
6262         }
6263     }
6264
6265     wname = ModeToWidgetName(oldmode);
6266     if (wname != NULL) {
6267         XtSetArg(args[0], XtNleftBitmap, None);
6268         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
6269     }
6270     wname = ModeToWidgetName(gameMode);
6271     if (wname != NULL) {
6272         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6273         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
6274     }
6275     oldmode = gameMode;
6276
6277     /* Maybe all the enables should be handled here, not just this one */
6278     XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
6279                    gameMode == Training || gameMode == PlayFromGameFile);
6280 }
6281
6282
6283 /*
6284  * Button/menu procedures
6285  */
6286 void ResetProc(w, event, prms, nprms)
6287      Widget w;
6288      XEvent *event;
6289      String *prms;
6290      Cardinal *nprms;
6291 {
6292     ResetGameEvent();
6293     AnalysisPopDown();
6294 }
6295
6296 int LoadGamePopUp(f, gameNumber, title)
6297      FILE *f;
6298      int gameNumber;
6299      char *title;
6300 {
6301     cmailMsgLoaded = FALSE;
6302     if (gameNumber == 0) {
6303         int error = GameListBuild(f);
6304         if (error) {
6305             DisplayError(_("Cannot build game list"), error);
6306         } else if (!ListEmpty(&gameList) &&
6307                    ((ListGame *) gameList.tailPred)->number > 1) {
6308             GameListPopUp(f, title);
6309             return TRUE;
6310         }
6311         GameListDestroy();
6312         gameNumber = 1;
6313     }
6314     return LoadGame(f, gameNumber, title, FALSE);
6315 }
6316
6317 void LoadGameProc(w, event, prms, nprms)
6318      Widget w;
6319      XEvent *event;
6320      String *prms;
6321      Cardinal *nprms;
6322 {
6323     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
6324         Reset(FALSE, TRUE);
6325     }
6326     FileNamePopUp(_("Load game file name?"), "", LoadGamePopUp, "rb");
6327 }
6328
6329 void LoadNextGameProc(w, event, prms, nprms)
6330      Widget w;
6331      XEvent *event;
6332      String *prms;
6333      Cardinal *nprms;
6334 {
6335     ReloadGame(1);
6336 }
6337
6338 void LoadPrevGameProc(w, event, prms, nprms)
6339      Widget w;
6340      XEvent *event;
6341      String *prms;
6342      Cardinal *nprms;
6343 {
6344     ReloadGame(-1);
6345 }
6346
6347 void ReloadGameProc(w, event, prms, nprms)
6348      Widget w;
6349      XEvent *event;
6350      String *prms;
6351      Cardinal *nprms;
6352 {
6353     ReloadGame(0);
6354 }
6355
6356 void LoadNextPositionProc(w, event, prms, nprms)
6357      Widget w;
6358      XEvent *event;
6359      String *prms;
6360      Cardinal *nprms;
6361 {
6362     ReloadPosition(1);
6363 }
6364
6365 void LoadPrevPositionProc(w, event, prms, nprms)
6366      Widget w;
6367      XEvent *event;
6368      String *prms;
6369      Cardinal *nprms;
6370 {
6371     ReloadPosition(-1);
6372 }
6373
6374 void ReloadPositionProc(w, event, prms, nprms)
6375      Widget w;
6376      XEvent *event;
6377      String *prms;
6378      Cardinal *nprms;
6379 {
6380     ReloadPosition(0);
6381 }
6382
6383 void LoadPositionProc(w, event, prms, nprms)
6384      Widget w;
6385      XEvent *event;
6386      String *prms;
6387      Cardinal *nprms;
6388 {
6389     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
6390         Reset(FALSE, TRUE);
6391     }
6392     FileNamePopUp(_("Load position file name?"), "", LoadPosition, "rb");
6393 }
6394
6395 void SaveGameProc(w, event, prms, nprms)
6396      Widget w;
6397      XEvent *event;
6398      String *prms;
6399      Cardinal *nprms;
6400 {
6401     FileNamePopUp(_("Save game file name?"),
6402                   DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
6403                   SaveGame, "a");
6404 }
6405
6406 void SavePositionProc(w, event, prms, nprms)
6407      Widget w;
6408      XEvent *event;
6409      String *prms;
6410      Cardinal *nprms;
6411 {
6412     FileNamePopUp(_("Save position file name?"),
6413                   DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
6414                   SavePosition, "a");
6415 }
6416
6417 void ReloadCmailMsgProc(w, event, prms, nprms)
6418      Widget w;
6419      XEvent *event;
6420      String *prms;
6421      Cardinal *nprms;
6422 {
6423     ReloadCmailMsgEvent(FALSE);
6424 }
6425
6426 void MailMoveProc(w, event, prms, nprms)
6427      Widget w;
6428      XEvent *event;
6429      String *prms;
6430      Cardinal *nprms;
6431 {
6432     MailMoveEvent();
6433 }
6434
6435 /* this variable is shared between CopyPositionProc and SendPositionSelection */
6436 static char *selected_fen_position=NULL;
6437
6438 static Boolean
6439 SendPositionSelection(Widget w, Atom *selection, Atom *target,
6440                  Atom *type_return, XtPointer *value_return,
6441                  unsigned long *length_return, int *format_return)
6442 {
6443   char *selection_tmp;
6444
6445   if (!selected_fen_position) return False; /* should never happen */
6446   if (*target == XA_STRING){
6447     /* note: since no XtSelectionDoneProc was registered, Xt will
6448      * automatically call XtFree on the value returned.  So have to
6449      * make a copy of it allocated with XtMalloc */
6450     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
6451     strcpy(selection_tmp, selected_fen_position);
6452
6453     *value_return=selection_tmp;
6454     *length_return=strlen(selection_tmp);
6455     *type_return=XA_STRING;
6456     *format_return = 8; /* bits per byte */
6457     return True;
6458   } else {
6459     return False;
6460   }
6461 }
6462
6463 /* note: when called from menu all parameters are NULL, so no clue what the
6464  * Widget which was clicked on was, or what the click event was
6465  */
6466 void CopyPositionProc(w, event, prms, nprms)
6467   Widget w;
6468   XEvent *event;
6469   String *prms;
6470   Cardinal *nprms;
6471   {
6472     int ret;
6473
6474     if (selected_fen_position) free(selected_fen_position);
6475     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
6476     if (!selected_fen_position) return;
6477     ret = XtOwnSelection(menuBarWidget, XA_PRIMARY,
6478                          CurrentTime,
6479                          SendPositionSelection,
6480                          NULL/* lose_ownership_proc */ ,
6481                          NULL/* transfer_done_proc */);
6482     if (!ret) {
6483       free(selected_fen_position);
6484       selected_fen_position=NULL;
6485     }
6486   }
6487
6488 /* function called when the data to Paste is ready */
6489 static void
6490 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
6491            Atom *type, XtPointer value, unsigned long *len, int *format)
6492 {
6493   char *fenstr=value;
6494   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
6495   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
6496   EditPositionPasteFEN(fenstr);
6497   XtFree(value);
6498 }
6499
6500 /* called when Paste Position button is pressed,
6501  * all parameters will be NULL */
6502 void PastePositionProc(w, event, prms, nprms)
6503   Widget w;
6504   XEvent *event;
6505   String *prms;
6506   Cardinal *nprms;
6507 {
6508     XtGetSelectionValue(menuBarWidget, XA_PRIMARY, XA_STRING,
6509       /* (XtSelectionCallbackProc) */ PastePositionCB,
6510       NULL, /* client_data passed to PastePositionCB */
6511
6512       /* better to use the time field from the event that triggered the
6513        * call to this function, but that isn't trivial to get
6514        */
6515       CurrentTime
6516     );
6517     return;
6518 }
6519
6520 static Boolean
6521 SendGameSelection(Widget w, Atom *selection, Atom *target,
6522                   Atom *type_return, XtPointer *value_return,
6523                   unsigned long *length_return, int *format_return)
6524 {
6525   char *selection_tmp;
6526
6527   if (*target == XA_STRING){
6528     FILE* f = fopen(gameCopyFilename, "r");
6529     long len;
6530     size_t count;
6531     if (f == NULL) return False;
6532     fseek(f, 0, 2);
6533     len = ftell(f);
6534     rewind(f);
6535     selection_tmp = XtMalloc(len + 1);
6536     count = fread(selection_tmp, 1, len, f);
6537     if (len != count) {
6538       XtFree(selection_tmp);
6539       return False;
6540     }
6541     selection_tmp[len] = NULLCHAR;
6542     *value_return = selection_tmp;
6543     *length_return = len;
6544     *type_return = XA_STRING;
6545     *format_return = 8; /* bits per byte */
6546     return True;
6547   } else {
6548     return False;
6549   }
6550 }
6551
6552 /* note: when called from menu all parameters are NULL, so no clue what the
6553  * Widget which was clicked on was, or what the click event was
6554  */
6555 void CopyGameProc(w, event, prms, nprms)
6556   Widget w;
6557   XEvent *event;
6558   String *prms;
6559   Cardinal *nprms;
6560 {
6561   int ret;
6562
6563   ret = SaveGameToFile(gameCopyFilename, FALSE);
6564   if (!ret) return;
6565
6566   ret = XtOwnSelection(menuBarWidget, XA_PRIMARY,
6567                        CurrentTime,
6568                        SendGameSelection,
6569                        NULL/* lose_ownership_proc */ ,
6570                        NULL/* transfer_done_proc */);
6571 }
6572
6573 /* function called when the data to Paste is ready */
6574 static void
6575 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
6576             Atom *type, XtPointer value, unsigned long *len, int *format)
6577 {
6578   FILE* f;
6579   if (value == NULL || *len == 0) {
6580     return; /* nothing had been selected to copy */
6581   }
6582   f = fopen(gamePasteFilename, "w");
6583   if (f == NULL) {
6584     DisplayError(_("Can't open temp file"), errno);
6585     return;
6586   }
6587   fwrite(value, 1, *len, f);
6588   fclose(f);
6589   XtFree(value);
6590   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
6591 }
6592
6593 /* called when Paste Game button is pressed,
6594  * all parameters will be NULL */
6595 void PasteGameProc(w, event, prms, nprms)
6596   Widget w;
6597   XEvent *event;
6598   String *prms;
6599   Cardinal *nprms;
6600 {
6601     XtGetSelectionValue(menuBarWidget, XA_PRIMARY, XA_STRING,
6602       /* (XtSelectionCallbackProc) */ PasteGameCB,
6603       NULL, /* client_data passed to PasteGameCB */
6604
6605       /* better to use the time field from the event that triggered the
6606        * call to this function, but that isn't trivial to get
6607        */
6608       CurrentTime
6609     );
6610     return;
6611 }
6612
6613
6614 void AutoSaveGame()
6615 {
6616     SaveGameProc(NULL, NULL, NULL, NULL);
6617 }
6618
6619
6620 void QuitProc(w, event, prms, nprms)
6621      Widget w;
6622      XEvent *event;
6623      String *prms;
6624      Cardinal *nprms;
6625 {
6626     ExitEvent(0);
6627 }
6628
6629 void PauseProc(w, event, prms, nprms)
6630      Widget w;
6631      XEvent *event;
6632      String *prms;
6633      Cardinal *nprms;
6634 {
6635     PauseEvent();
6636 }
6637
6638
6639 void MachineBlackProc(w, event, prms, nprms)
6640      Widget w;
6641      XEvent *event;
6642      String *prms;
6643      Cardinal *nprms;
6644 {
6645     MachineBlackEvent();
6646 }
6647
6648 void MachineWhiteProc(w, event, prms, nprms)
6649      Widget w;
6650      XEvent *event;
6651      String *prms;
6652      Cardinal *nprms;
6653 {
6654     MachineWhiteEvent();
6655 }
6656
6657 void AnalyzeModeProc(w, event, prms, nprms)
6658      Widget w;
6659      XEvent *event;
6660      String *prms;
6661      Cardinal *nprms;
6662 {
6663     char buf[MSG_SIZ];
6664
6665     if (!first.analysisSupport) {
6666       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6667       DisplayError(buf, 0);
6668       return;
6669     }
6670     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
6671     if (appData.icsActive) {
6672         if (gameMode != IcsObserving) {
6673             sprintf(buf,_("You are not observing a game"));
6674             DisplayError(buf, 0);
6675             /* secure check */
6676             if (appData.icsEngineAnalyze) {
6677                 if (appData.debugMode)
6678                     fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
6679                 ExitAnalyzeMode();
6680                 ModeHighlight();
6681             }
6682             return;
6683         }
6684         /* if enable, use want disable icsEngineAnalyze */
6685         if (appData.icsEngineAnalyze) {
6686                 ExitAnalyzeMode();
6687                 ModeHighlight();
6688                 return;
6689         }
6690         appData.icsEngineAnalyze = TRUE;
6691         if (appData.debugMode)
6692             fprintf(debugFP, _("ICS engine analyze starting... \n"));
6693     }
6694     if (!appData.showThinking)
6695       ShowThinkingProc(w,event,prms,nprms);
6696
6697     AnalyzeModeEvent();
6698 }
6699
6700 void AnalyzeFileProc(w, event, prms, nprms)
6701      Widget w;
6702      XEvent *event;
6703      String *prms;
6704      Cardinal *nprms;
6705 {
6706     if (!first.analysisSupport) {
6707       char buf[MSG_SIZ];
6708       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6709       DisplayError(buf, 0);
6710       return;
6711     }
6712     Reset(FALSE, TRUE);
6713
6714     if (!appData.showThinking)
6715       ShowThinkingProc(w,event,prms,nprms);
6716
6717     AnalyzeFileEvent();
6718     FileNamePopUp(_("File to analyze"), "", LoadGamePopUp, "rb");
6719     AnalysisPeriodicEvent(1);
6720 }
6721
6722 void TwoMachinesProc(w, event, prms, nprms)
6723      Widget w;
6724      XEvent *event;
6725      String *prms;
6726      Cardinal *nprms;
6727 {
6728     TwoMachinesEvent();
6729 }
6730
6731 void IcsClientProc(w, event, prms, nprms)
6732      Widget w;
6733      XEvent *event;
6734      String *prms;
6735      Cardinal *nprms;
6736 {
6737     IcsClientEvent();
6738 }
6739
6740 void EditGameProc(w, event, prms, nprms)
6741      Widget w;
6742      XEvent *event;
6743      String *prms;
6744      Cardinal *nprms;
6745 {
6746     EditGameEvent();
6747 }
6748
6749 void EditPositionProc(w, event, prms, nprms)
6750      Widget w;
6751      XEvent *event;
6752      String *prms;
6753      Cardinal *nprms;
6754 {
6755     EditPositionEvent();
6756 }
6757
6758 void TrainingProc(w, event, prms, nprms)
6759      Widget w;
6760      XEvent *event;
6761      String *prms;
6762      Cardinal *nprms;
6763 {
6764     TrainingEvent();
6765 }
6766
6767 void EditCommentProc(w, event, prms, nprms)
6768      Widget w;
6769      XEvent *event;
6770      String *prms;
6771      Cardinal *nprms;
6772 {
6773     if (editUp) {
6774         EditCommentPopDown();
6775     } else {
6776         EditCommentEvent();
6777     }
6778 }
6779
6780 void IcsInputBoxProc(w, event, prms, nprms)
6781      Widget w;
6782      XEvent *event;
6783      String *prms;
6784      Cardinal *nprms;
6785 {
6786     if (ICSInputBoxUp) {
6787         ICSInputBoxPopDown();
6788     } else {
6789         ICSInputBoxPopUp();
6790     }
6791 }
6792
6793 void AcceptProc(w, event, prms, nprms)
6794      Widget w;
6795      XEvent *event;
6796      String *prms;
6797      Cardinal *nprms;
6798 {
6799     AcceptEvent();
6800 }
6801
6802 void DeclineProc(w, event, prms, nprms)
6803      Widget w;
6804      XEvent *event;
6805      String *prms;
6806      Cardinal *nprms;
6807 {
6808     DeclineEvent();
6809 }
6810
6811 void RematchProc(w, event, prms, nprms)
6812      Widget w;
6813      XEvent *event;
6814      String *prms;
6815      Cardinal *nprms;
6816 {
6817     RematchEvent();
6818 }
6819
6820 void CallFlagProc(w, event, prms, nprms)
6821      Widget w;
6822      XEvent *event;
6823      String *prms;
6824      Cardinal *nprms;
6825 {
6826     CallFlagEvent();
6827 }
6828
6829 void DrawProc(w, event, prms, nprms)
6830      Widget w;
6831      XEvent *event;
6832      String *prms;
6833      Cardinal *nprms;
6834 {
6835     DrawEvent();
6836 }
6837
6838 void AbortProc(w, event, prms, nprms)
6839      Widget w;
6840      XEvent *event;
6841      String *prms;
6842      Cardinal *nprms;
6843 {
6844     AbortEvent();
6845 }
6846
6847 void AdjournProc(w, event, prms, nprms)
6848      Widget w;
6849      XEvent *event;
6850      String *prms;
6851      Cardinal *nprms;
6852 {
6853     AdjournEvent();
6854 }
6855
6856 void ResignProc(w, event, prms, nprms)
6857      Widget w;
6858      XEvent *event;
6859      String *prms;
6860      Cardinal *nprms;
6861 {
6862     ResignEvent();
6863 }
6864
6865 void AdjuWhiteProc(w, event, prms, nprms)
6866      Widget w;
6867      XEvent *event;
6868      String *prms;
6869      Cardinal *nprms;
6870 {
6871     UserAdjudicationEvent(+1);
6872 }
6873
6874 void AdjuBlackProc(w, event, prms, nprms)
6875      Widget w;
6876      XEvent *event;
6877      String *prms;
6878      Cardinal *nprms;
6879 {
6880     UserAdjudicationEvent(-1);
6881 }
6882
6883 void AdjuDrawProc(w, event, prms, nprms)
6884      Widget w;
6885      XEvent *event;
6886      String *prms;
6887      Cardinal *nprms;
6888 {
6889     UserAdjudicationEvent(0);
6890 }
6891
6892 void EnterKeyProc(w, event, prms, nprms)
6893      Widget w;
6894      XEvent *event;
6895      String *prms;
6896      Cardinal *nprms;
6897 {
6898     if (ICSInputBoxUp == True)
6899       ICSInputSendText();
6900 }
6901
6902 void StopObservingProc(w, event, prms, nprms)
6903      Widget w;
6904      XEvent *event;
6905      String *prms;
6906      Cardinal *nprms;
6907 {
6908     StopObservingEvent();
6909 }
6910
6911 void StopExaminingProc(w, event, prms, nprms)
6912      Widget w;
6913      XEvent *event;
6914      String *prms;
6915      Cardinal *nprms;
6916 {
6917     StopExaminingEvent();
6918 }
6919
6920
6921 void ForwardProc(w, event, prms, nprms)
6922      Widget w;
6923      XEvent *event;
6924      String *prms;
6925      Cardinal *nprms;
6926 {
6927     ForwardEvent();
6928 }
6929
6930
6931 void BackwardProc(w, event, prms, nprms)
6932      Widget w;
6933      XEvent *event;
6934      String *prms;
6935      Cardinal *nprms;
6936 {
6937     BackwardEvent();
6938 }
6939
6940 void ToStartProc(w, event, prms, nprms)
6941      Widget w;
6942      XEvent *event;
6943      String *prms;
6944      Cardinal *nprms;
6945 {
6946     ToStartEvent();
6947 }
6948
6949 void ToEndProc(w, event, prms, nprms)
6950      Widget w;
6951      XEvent *event;
6952      String *prms;
6953      Cardinal *nprms;
6954 {
6955     ToEndEvent();
6956 }
6957
6958 void RevertProc(w, event, prms, nprms)
6959      Widget w;
6960      XEvent *event;
6961      String *prms;
6962      Cardinal *nprms;
6963 {
6964     RevertEvent();
6965 }
6966
6967 void TruncateGameProc(w, event, prms, nprms)
6968      Widget w;
6969      XEvent *event;
6970      String *prms;
6971      Cardinal *nprms;
6972 {
6973     TruncateGameEvent();
6974 }
6975 void RetractMoveProc(w, event, prms, nprms)
6976      Widget w;
6977      XEvent *event;
6978      String *prms;
6979      Cardinal *nprms;
6980 {
6981     RetractMoveEvent();
6982 }
6983
6984 void MoveNowProc(w, event, prms, nprms)
6985      Widget w;
6986      XEvent *event;
6987      String *prms;
6988      Cardinal *nprms;
6989 {
6990     MoveNowEvent();
6991 }
6992
6993
6994 void AlwaysQueenProc(w, event, prms, nprms)
6995      Widget w;
6996      XEvent *event;
6997      String *prms;
6998      Cardinal *nprms;
6999 {
7000     Arg args[16];
7001
7002     appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
7003
7004     if (appData.alwaysPromoteToQueen) {
7005         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7006     } else {
7007         XtSetArg(args[0], XtNleftBitmap, None);
7008     }
7009     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
7010                 args, 1);
7011 }
7012
7013 void AnimateDraggingProc(w, event, prms, nprms)
7014      Widget w;
7015      XEvent *event;
7016      String *prms;
7017      Cardinal *nprms;
7018 {
7019     Arg args[16];
7020
7021     appData.animateDragging = !appData.animateDragging;
7022
7023     if (appData.animateDragging) {
7024         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7025         CreateAnimVars();
7026     } else {
7027         XtSetArg(args[0], XtNleftBitmap, None);
7028     }
7029     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
7030                 args, 1);
7031 }
7032
7033 void AnimateMovingProc(w, event, prms, nprms)
7034      Widget w;
7035      XEvent *event;
7036      String *prms;
7037      Cardinal *nprms;
7038 {
7039     Arg args[16];
7040
7041     appData.animate = !appData.animate;
7042
7043     if (appData.animate) {
7044         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7045         CreateAnimVars();
7046     } else {
7047         XtSetArg(args[0], XtNleftBitmap, None);
7048     }
7049     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
7050                 args, 1);
7051 }
7052
7053 void AutocommProc(w, event, prms, nprms)
7054      Widget w;
7055      XEvent *event;
7056      String *prms;
7057      Cardinal *nprms;
7058 {
7059     Arg args[16];
7060
7061     appData.autoComment = !appData.autoComment;
7062
7063     if (appData.autoComment) {
7064         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7065     } else {
7066         XtSetArg(args[0], XtNleftBitmap, None);
7067     }
7068     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
7069                 args, 1);
7070 }
7071
7072
7073 void AutoflagProc(w, event, prms, nprms)
7074      Widget w;
7075      XEvent *event;
7076      String *prms;
7077      Cardinal *nprms;
7078 {
7079     Arg args[16];
7080
7081     appData.autoCallFlag = !appData.autoCallFlag;
7082
7083     if (appData.autoCallFlag) {
7084         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7085     } else {
7086         XtSetArg(args[0], XtNleftBitmap, None);
7087     }
7088     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
7089                 args, 1);
7090 }
7091
7092 void AutoflipProc(w, event, prms, nprms)
7093      Widget w;
7094      XEvent *event;
7095      String *prms;
7096      Cardinal *nprms;
7097 {
7098     Arg args[16];
7099
7100     appData.autoFlipView = !appData.autoFlipView;
7101
7102     if (appData.autoFlipView) {
7103         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7104     } else {
7105         XtSetArg(args[0], XtNleftBitmap, None);
7106     }
7107     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
7108                 args, 1);
7109 }
7110
7111 void AutobsProc(w, event, prms, nprms)
7112      Widget w;
7113      XEvent *event;
7114      String *prms;
7115      Cardinal *nprms;
7116 {
7117     Arg args[16];
7118
7119     appData.autoObserve = !appData.autoObserve;
7120
7121     if (appData.autoObserve) {
7122         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7123     } else {
7124         XtSetArg(args[0], XtNleftBitmap, None);
7125     }
7126     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
7127                 args, 1);
7128 }
7129
7130 void AutoraiseProc(w, event, prms, nprms)
7131      Widget w;
7132      XEvent *event;
7133      String *prms;
7134      Cardinal *nprms;
7135 {
7136     Arg args[16];
7137
7138     appData.autoRaiseBoard = !appData.autoRaiseBoard;
7139
7140     if (appData.autoRaiseBoard) {
7141         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7142     } else {
7143         XtSetArg(args[0], XtNleftBitmap, None);
7144     }
7145     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Raise Board"),
7146                 args, 1);
7147 }
7148
7149 void AutosaveProc(w, event, prms, nprms)
7150      Widget w;
7151      XEvent *event;
7152      String *prms;
7153      Cardinal *nprms;
7154 {
7155     Arg args[16];
7156
7157     appData.autoSaveGames = !appData.autoSaveGames;
7158
7159     if (appData.autoSaveGames) {
7160         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7161     } else {
7162         XtSetArg(args[0], XtNleftBitmap, None);
7163     }
7164     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
7165                 args, 1);
7166 }
7167
7168 void BlindfoldProc(w, event, prms, nprms)
7169      Widget w;
7170      XEvent *event;
7171      String *prms;
7172      Cardinal *nprms;
7173 {
7174     Arg args[16];
7175
7176     appData.blindfold = !appData.blindfold;
7177
7178     if (appData.blindfold) {
7179         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7180     } else {
7181         XtSetArg(args[0], XtNleftBitmap, None);
7182     }
7183     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
7184                 args, 1);
7185
7186     DrawPosition(True, NULL);
7187 }
7188
7189 void TestLegalityProc(w, event, prms, nprms)
7190      Widget w;
7191      XEvent *event;
7192      String *prms;
7193      Cardinal *nprms;
7194 {
7195     Arg args[16];
7196
7197     appData.testLegality = !appData.testLegality;
7198
7199     if (appData.testLegality) {
7200         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7201     } else {
7202         XtSetArg(args[0], XtNleftBitmap, None);
7203     }
7204     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
7205                 args, 1);
7206 }
7207
7208
7209 void FlashMovesProc(w, event, prms, nprms)
7210      Widget w;
7211      XEvent *event;
7212      String *prms;
7213      Cardinal *nprms;
7214 {
7215     Arg args[16];
7216
7217     if (appData.flashCount == 0) {
7218         appData.flashCount = 3;
7219     } else {
7220         appData.flashCount = -appData.flashCount;
7221     }
7222
7223     if (appData.flashCount > 0) {
7224         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7225     } else {
7226         XtSetArg(args[0], XtNleftBitmap, None);
7227     }
7228     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
7229                 args, 1);
7230 }
7231
7232 void FlipViewProc(w, event, prms, nprms)
7233      Widget w;
7234      XEvent *event;
7235      String *prms;
7236      Cardinal *nprms;
7237 {
7238     flipView = !flipView;
7239     DrawPosition(True, NULL);
7240 }
7241
7242 void GetMoveListProc(w, event, prms, nprms)
7243      Widget w;
7244      XEvent *event;
7245      String *prms;
7246      Cardinal *nprms;
7247 {
7248     Arg args[16];
7249
7250     appData.getMoveList = !appData.getMoveList;
7251
7252     if (appData.getMoveList) {
7253         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7254         GetMoveListEvent();
7255     } else {
7256         XtSetArg(args[0], XtNleftBitmap, None);
7257     }
7258     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
7259                 args, 1);
7260 }
7261
7262 #if HIGHDRAG
7263 void HighlightDraggingProc(w, event, prms, nprms)
7264      Widget w;
7265      XEvent *event;
7266      String *prms;
7267      Cardinal *nprms;
7268 {
7269     Arg args[16];
7270
7271     appData.highlightDragging = !appData.highlightDragging;
7272
7273     if (appData.highlightDragging) {
7274         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7275     } else {
7276         XtSetArg(args[0], XtNleftBitmap, None);
7277     }
7278     XtSetValues(XtNameToWidget(menuBarWidget,
7279                                "menuOptions.Highlight Dragging"), args, 1);
7280 }
7281 #endif
7282
7283 void HighlightLastMoveProc(w, event, prms, nprms)
7284      Widget w;
7285      XEvent *event;
7286      String *prms;
7287      Cardinal *nprms;
7288 {
7289     Arg args[16];
7290
7291     appData.highlightLastMove = !appData.highlightLastMove;
7292
7293     if (appData.highlightLastMove) {
7294         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7295     } else {
7296         XtSetArg(args[0], XtNleftBitmap, None);
7297     }
7298     XtSetValues(XtNameToWidget(menuBarWidget,
7299                                "menuOptions.Highlight Last Move"), args, 1);
7300 }
7301
7302 void IcsAlarmProc(w, event, prms, nprms)
7303      Widget w;
7304      XEvent *event;
7305      String *prms;
7306      Cardinal *nprms;
7307 {
7308     Arg args[16];
7309
7310     appData.icsAlarm = !appData.icsAlarm;
7311
7312     if (appData.icsAlarm) {
7313         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7314     } else {
7315         XtSetArg(args[0], XtNleftBitmap, None);
7316     }
7317     XtSetValues(XtNameToWidget(menuBarWidget,
7318                                "menuOptions.ICS Alarm"), args, 1);
7319 }
7320
7321 void MoveSoundProc(w, event, prms, nprms)
7322      Widget w;
7323      XEvent *event;
7324      String *prms;
7325      Cardinal *nprms;
7326 {
7327     Arg args[16];
7328
7329     appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
7330
7331     if (appData.ringBellAfterMoves) {
7332         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7333     } else {
7334         XtSetArg(args[0], XtNleftBitmap, None);
7335     }
7336     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
7337                 args, 1);
7338 }
7339
7340
7341 void OldSaveStyleProc(w, event, prms, nprms)
7342      Widget w;
7343      XEvent *event;
7344      String *prms;
7345      Cardinal *nprms;
7346 {
7347     Arg args[16];
7348
7349     appData.oldSaveStyle = !appData.oldSaveStyle;
7350
7351     if (appData.oldSaveStyle) {
7352         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7353     } else {
7354         XtSetArg(args[0], XtNleftBitmap, None);
7355     }
7356     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Old Save Style"),
7357                 args, 1);
7358 }
7359
7360 void PeriodicUpdatesProc(w, event, prms, nprms)
7361      Widget w;
7362      XEvent *event;
7363      String *prms;
7364      Cardinal *nprms;
7365 {
7366     Arg args[16];
7367
7368     PeriodicUpdatesEvent(!appData.periodicUpdates);
7369
7370     if (appData.periodicUpdates) {
7371         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7372     } else {
7373         XtSetArg(args[0], XtNleftBitmap, None);
7374     }
7375     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
7376                 args, 1);
7377 }
7378
7379 void PonderNextMoveProc(w, event, prms, nprms)
7380      Widget w;
7381      XEvent *event;
7382      String *prms;
7383      Cardinal *nprms;
7384 {
7385     Arg args[16];
7386
7387     PonderNextMoveEvent(!appData.ponderNextMove);
7388
7389     if (appData.ponderNextMove) {
7390         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7391     } else {
7392         XtSetArg(args[0], XtNleftBitmap, None);
7393     }
7394     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
7395                 args, 1);
7396 }
7397
7398 void PopupExitMessageProc(w, event, prms, nprms)
7399      Widget w;
7400      XEvent *event;
7401      String *prms;
7402      Cardinal *nprms;
7403 {
7404     Arg args[16];
7405
7406     appData.popupExitMessage = !appData.popupExitMessage;
7407
7408     if (appData.popupExitMessage) {
7409         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7410     } else {
7411         XtSetArg(args[0], XtNleftBitmap, None);
7412     }
7413     XtSetValues(XtNameToWidget(menuBarWidget,
7414                                "menuOptions.Popup Exit Message"), args, 1);
7415 }
7416
7417 void PopupMoveErrorsProc(w, event, prms, nprms)
7418      Widget w;
7419      XEvent *event;
7420      String *prms;
7421      Cardinal *nprms;
7422 {
7423     Arg args[16];
7424
7425     appData.popupMoveErrors = !appData.popupMoveErrors;
7426
7427     if (appData.popupMoveErrors) {
7428         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7429     } else {
7430         XtSetArg(args[0], XtNleftBitmap, None);
7431     }
7432     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
7433                 args, 1);
7434 }
7435
7436 void PremoveProc(w, event, prms, nprms)
7437      Widget w;
7438      XEvent *event;
7439      String *prms;
7440      Cardinal *nprms;
7441 {
7442     Arg args[16];
7443
7444     appData.premove = !appData.premove;
7445
7446     if (appData.premove) {
7447         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7448     } else {
7449         XtSetArg(args[0], XtNleftBitmap, None);
7450     }
7451     XtSetValues(XtNameToWidget(menuBarWidget,
7452                                "menuOptions.Premove"), args, 1);
7453 }
7454
7455 void QuietPlayProc(w, event, prms, nprms)
7456      Widget w;
7457      XEvent *event;
7458      String *prms;
7459      Cardinal *nprms;
7460 {
7461     Arg args[16];
7462
7463     appData.quietPlay = !appData.quietPlay;
7464
7465     if (appData.quietPlay) {
7466         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7467     } else {
7468         XtSetArg(args[0], XtNleftBitmap, None);
7469     }
7470     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Quiet Play"),
7471                 args, 1);
7472 }
7473
7474 void ShowCoordsProc(w, event, prms, nprms)
7475      Widget w;
7476      XEvent *event;
7477      String *prms;
7478      Cardinal *nprms;
7479 {
7480     Arg args[16];
7481
7482     appData.showCoords = !appData.showCoords;
7483
7484     if (appData.showCoords) {
7485         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7486     } else {
7487         XtSetArg(args[0], XtNleftBitmap, None);
7488     }
7489     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
7490                 args, 1);
7491
7492     DrawPosition(True, NULL);
7493 }
7494
7495 void ShowThinkingProc(w, event, prms, nprms)
7496      Widget w;
7497      XEvent *event;
7498      String *prms;
7499      Cardinal *nprms;
7500 {
7501     Arg args[16];
7502
7503     appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
7504     ShowThinkingEvent();
7505 #if 0
7506     // [HGM] thinking: currently no suc menu item; replaced by Hide Thinking (From Human)
7507     if (appData.showThinking) {
7508         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7509     } else {
7510         XtSetArg(args[0], XtNleftBitmap, None);
7511     }
7512     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Thinking"),
7513                 args, 1);
7514 #endif
7515 }
7516
7517 void HideThinkingProc(w, event, prms, nprms)
7518      Widget w;
7519      XEvent *event;
7520      String *prms;
7521      Cardinal *nprms;
7522 {
7523     Arg args[16];
7524
7525     appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
7526     ShowThinkingEvent();
7527
7528     if (appData.hideThinkingFromHuman) {
7529         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7530     } else {
7531         XtSetArg(args[0], XtNleftBitmap, None);
7532     }
7533     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
7534                 args, 1);
7535 }
7536
7537 void InfoProc(w, event, prms, nprms)
7538      Widget w;
7539      XEvent *event;
7540      String *prms;
7541      Cardinal *nprms;
7542 {
7543     char buf[MSG_SIZ];
7544     snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
7545             INFODIR, INFOFILE);
7546     system(buf);
7547 }
7548
7549 void ManProc(w, event, prms, nprms)
7550      Widget w;
7551      XEvent *event;
7552      String *prms;
7553      Cardinal *nprms;
7554 {
7555     char buf[MSG_SIZ];
7556     String name;
7557     if (nprms && *nprms > 0)
7558       name = prms[0];
7559     else
7560       name = "xboard";
7561     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
7562     system(buf);
7563 }
7564
7565 void HintProc(w, event, prms, nprms)
7566      Widget w;
7567      XEvent *event;
7568      String *prms;
7569      Cardinal *nprms;
7570 {
7571     HintEvent();
7572 }
7573
7574 void BookProc(w, event, prms, nprms)
7575      Widget w;
7576      XEvent *event;
7577      String *prms;
7578      Cardinal *nprms;
7579 {
7580     BookEvent();
7581 }
7582
7583 void AboutProc(w, event, prms, nprms)
7584      Widget w;
7585      XEvent *event;
7586      String *prms;
7587      Cardinal *nprms;
7588 {
7589     char buf[MSG_SIZ];
7590 #if ZIPPY
7591     char *zippy = " (with Zippy code)";
7592 #else
7593     char *zippy = "";
7594 #endif
7595     snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
7596             programVersion, zippy,
7597             "Copyright 1991 Digital Equipment Corporation",
7598             "Enhancements Copyright 1992-2009 Free Software Foundation",
7599             "Enhancements Copyright 2005 Alessandro Scotti",
7600             PACKAGE, " is free software and carries NO WARRANTY;",
7601             "see the file COPYING for more information.");
7602     ErrorPopUp(_("About XBoard"), buf, FALSE);
7603 }
7604
7605 void DebugProc(w, event, prms, nprms)
7606      Widget w;
7607      XEvent *event;
7608      String *prms;
7609      Cardinal *nprms;
7610 {
7611     appData.debugMode = !appData.debugMode;
7612 }
7613
7614 void AboutGameProc(w, event, prms, nprms)
7615      Widget w;
7616      XEvent *event;
7617      String *prms;
7618      Cardinal *nprms;
7619 {
7620     AboutGameEvent();
7621 }
7622
7623 void NothingProc(w, event, prms, nprms)
7624      Widget w;
7625      XEvent *event;
7626      String *prms;
7627      Cardinal *nprms;
7628 {
7629     return;
7630 }
7631
7632 void Iconify(w, event, prms, nprms)
7633      Widget w;
7634      XEvent *event;
7635      String *prms;
7636      Cardinal *nprms;
7637 {
7638     Arg args[16];
7639
7640     fromX = fromY = -1;
7641     XtSetArg(args[0], XtNiconic, True);
7642     XtSetValues(shellWidget, args, 1);
7643 }
7644
7645 void DisplayMessage(message, extMessage)
7646      char *message, *extMessage;
7647 {
7648   /* display a message in the message widget */
7649   
7650   char buf[MSG_SIZ];
7651   Arg arg;
7652   
7653   if (extMessage) 
7654     {
7655       if (*message) 
7656         {
7657           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
7658           message = buf;
7659         } 
7660       else 
7661         {
7662           message = extMessage;
7663         };
7664     };
7665   
7666   /* need to test if messageWidget already exists, since this function
7667      can also be called during the startup, if for example a Xresource
7668      is not set up correctly */
7669   if(messageWidget)
7670     {
7671       XtSetArg(arg, XtNlabel, message);
7672       XtSetValues(messageWidget, &arg, 1);
7673     };
7674   
7675   return;
7676 }
7677
7678 void DisplayTitle(text)
7679      char *text;
7680 {
7681     Arg args[16];
7682     int i;
7683     char title[MSG_SIZ];
7684     char icon[MSG_SIZ];
7685
7686     if (text == NULL) text = "";
7687
7688     if (appData.titleInWindow) {
7689         i = 0;
7690         XtSetArg(args[i], XtNlabel, text);   i++;
7691         XtSetValues(titleWidget, args, i);
7692     }
7693
7694     if (*text != NULLCHAR) {
7695         strcpy(icon, text);
7696         strcpy(title, text);
7697     } else if (appData.icsActive) {
7698         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
7699         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
7700     } else if (appData.cmailGameName[0] != NULLCHAR) {
7701         snprintf(icon, sizeof(icon), "%s", "CMail");
7702         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
7703 #ifdef GOTHIC
7704     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
7705     } else if (gameInfo.variant == VariantGothic) {
7706         strcpy(icon, programName);
7707         strcpy(title, GOTHIC);
7708 #endif
7709 #ifdef FALCON
7710     } else if (gameInfo.variant == VariantFalcon) {
7711         strcpy(icon, programName);
7712         strcpy(title, FALCON);
7713 #endif
7714     } else if (appData.noChessProgram) {
7715         strcpy(icon, programName);
7716         strcpy(title, programName);
7717     } else {
7718         strcpy(icon, first.tidy);
7719         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
7720     }
7721     i = 0;
7722     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
7723     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
7724     XtSetValues(shellWidget, args, i);
7725 }
7726
7727
7728 void DisplayError(message, error)
7729      String message;
7730      int error;
7731 {
7732     char buf[MSG_SIZ];
7733
7734     if (error == 0) {
7735         if (appData.debugMode || appData.matchMode) {
7736             fprintf(stderr, "%s: %s\n", programName, message);
7737         }
7738     } else {
7739         if (appData.debugMode || appData.matchMode) {
7740             fprintf(stderr, "%s: %s: %s\n",
7741                     programName, message, strerror(error));
7742         }
7743         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7744         message = buf;
7745     }
7746     ErrorPopUp(_("Error"), message, FALSE);
7747 }
7748
7749
7750 void DisplayMoveError(message)
7751      String message;
7752 {
7753     fromX = fromY = -1;
7754     ClearHighlights();
7755     DrawPosition(FALSE, NULL);
7756     if (appData.debugMode || appData.matchMode) {
7757         fprintf(stderr, "%s: %s\n", programName, message);
7758     }
7759     if (appData.popupMoveErrors) {
7760         ErrorPopUp(_("Error"), message, FALSE);
7761     } else {
7762         DisplayMessage(message, "");
7763     }
7764 }
7765
7766
7767 void DisplayFatalError(message, error, status)
7768      String message;
7769      int error, status;
7770 {
7771     char buf[MSG_SIZ];
7772
7773     errorExitStatus = status;
7774     if (error == 0) {
7775         fprintf(stderr, "%s: %s\n", programName, message);
7776     } else {
7777         fprintf(stderr, "%s: %s: %s\n",
7778                 programName, message, strerror(error));
7779         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7780         message = buf;
7781     }
7782     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7783       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7784     } else {
7785       ExitEvent(status);
7786     }
7787 }
7788
7789 void DisplayInformation(message)
7790      String message;
7791 {
7792     ErrorPopDown();
7793     ErrorPopUp(_("Information"), message, TRUE);
7794 }
7795
7796 void DisplayNote(message)
7797      String message;
7798 {
7799     ErrorPopDown();
7800     ErrorPopUp(_("Note"), message, FALSE);
7801 }
7802
7803 static int
7804 NullXErrorCheck(dpy, error_event)
7805      Display *dpy;
7806      XErrorEvent *error_event;
7807 {
7808     return 0;
7809 }
7810
7811 void DisplayIcsInteractionTitle(message)
7812      String message;
7813 {
7814   if (oldICSInteractionTitle == NULL) {
7815     /* Magic to find the old window title, adapted from vim */
7816     char *wina = getenv("WINDOWID");
7817     if (wina != NULL) {
7818       Window win = (Window) atoi(wina);
7819       Window root, parent, *children;
7820       unsigned int nchildren;
7821       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7822       for (;;) {
7823         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7824         if (!XQueryTree(xDisplay, win, &root, &parent,
7825                         &children, &nchildren)) break;
7826         if (children) XFree((void *)children);
7827         if (parent == root || parent == 0) break;
7828         win = parent;
7829       }
7830       XSetErrorHandler(oldHandler);
7831     }
7832     if (oldICSInteractionTitle == NULL) {
7833       oldICSInteractionTitle = "xterm";
7834     }
7835   }
7836   printf("\033]0;%s\007", message);
7837   fflush(stdout);
7838 }
7839
7840 char pendingReplyPrefix[MSG_SIZ];
7841 ProcRef pendingReplyPR;
7842
7843 void AskQuestionProc(w, event, prms, nprms)
7844      Widget w;
7845      XEvent *event;
7846      String *prms;
7847      Cardinal *nprms;
7848 {
7849     if (*nprms != 4) {
7850         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7851                 *nprms);
7852         return;
7853     }
7854     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7855 }
7856
7857 void AskQuestionPopDown()
7858 {
7859     if (!askQuestionUp) return;
7860     XtPopdown(askQuestionShell);
7861     XtDestroyWidget(askQuestionShell);
7862     askQuestionUp = False;
7863 }
7864
7865 void AskQuestionReplyAction(w, event, prms, nprms)
7866      Widget w;
7867      XEvent *event;
7868      String *prms;
7869      Cardinal *nprms;
7870 {
7871     char buf[MSG_SIZ];
7872     int err;
7873     String reply;
7874
7875     reply = XawDialogGetValueString(w = XtParent(w));
7876     strcpy(buf, pendingReplyPrefix);
7877     if (*buf) strcat(buf, " ");
7878     strcat(buf, reply);
7879     strcat(buf, "\n");
7880     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7881     AskQuestionPopDown();
7882
7883     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7884 }
7885
7886 void AskQuestionCallback(w, client_data, call_data)
7887      Widget w;
7888      XtPointer client_data, call_data;
7889 {
7890     String name;
7891     Arg args[16];
7892
7893     XtSetArg(args[0], XtNlabel, &name);
7894     XtGetValues(w, args, 1);
7895
7896     if (strcmp(name, _("cancel")) == 0) {
7897         AskQuestionPopDown();
7898     } else {
7899         AskQuestionReplyAction(w, NULL, NULL, NULL);
7900     }
7901 }
7902
7903 void AskQuestion(title, question, replyPrefix, pr)
7904      char *title, *question, *replyPrefix;
7905      ProcRef pr;
7906 {
7907     Arg args[16];
7908     Widget popup, layout, dialog, edit;
7909     Window root, child;
7910     int x, y, i;
7911     int win_x, win_y;
7912     unsigned int mask;
7913
7914     strcpy(pendingReplyPrefix, replyPrefix);
7915     pendingReplyPR = pr;
7916
7917     i = 0;
7918     XtSetArg(args[i], XtNresizable, True); i++;
7919     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7920     askQuestionShell = popup =
7921       XtCreatePopupShell(title, transientShellWidgetClass,
7922                          shellWidget, args, i);
7923
7924     layout =
7925       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7926                             layoutArgs, XtNumber(layoutArgs));
7927
7928     i = 0;
7929     XtSetArg(args[i], XtNlabel, question); i++;
7930     XtSetArg(args[i], XtNvalue, ""); i++;
7931     XtSetArg(args[i], XtNborderWidth, 0); i++;
7932     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7933                                    layout, args, i);
7934
7935     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7936                        (XtPointer) dialog);
7937     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7938                        (XtPointer) dialog);
7939
7940     XtRealizeWidget(popup);
7941     CatchDeleteWindow(popup, "AskQuestionPopDown");
7942
7943     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7944                   &x, &y, &win_x, &win_y, &mask);
7945
7946     XtSetArg(args[0], XtNx, x - 10);
7947     XtSetArg(args[1], XtNy, y - 30);
7948     XtSetValues(popup, args, 2);
7949
7950     XtPopup(popup, XtGrabExclusive);
7951     askQuestionUp = True;
7952
7953     edit = XtNameToWidget(dialog, "*value");
7954     XtSetKeyboardFocus(popup, edit);
7955 }
7956
7957
7958 void
7959 PlaySound(name)
7960      char *name;
7961 {
7962   if (*name == NULLCHAR) {
7963     return;
7964   } else if (strcmp(name, "$") == 0) {
7965     putc(BELLCHAR, stderr);
7966   } else {
7967     char buf[2048];
7968     snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
7969     system(buf);
7970   }
7971 }
7972
7973 void
7974 RingBell()
7975 {
7976   PlaySound(appData.soundMove);
7977 }
7978
7979 void
7980 PlayIcsWinSound()
7981 {
7982   PlaySound(appData.soundIcsWin);
7983 }
7984
7985 void
7986 PlayIcsLossSound()
7987 {
7988   PlaySound(appData.soundIcsLoss);
7989 }
7990
7991 void
7992 PlayIcsDrawSound()
7993 {
7994   PlaySound(appData.soundIcsDraw);
7995 }
7996
7997 void
7998 PlayIcsUnfinishedSound()
7999 {
8000   PlaySound(appData.soundIcsUnfinished);
8001 }
8002
8003 void
8004 PlayAlarmSound()
8005 {
8006   PlaySound(appData.soundIcsAlarm);
8007 }
8008
8009 void
8010 EchoOn()
8011 {
8012     system("stty echo");
8013 }
8014
8015 void
8016 EchoOff()
8017 {
8018     system("stty -echo");
8019 }
8020
8021 void
8022 Colorize(cc, continuation)
8023      ColorClass cc;
8024      int continuation;
8025 {
8026     char buf[MSG_SIZ];
8027     int count, outCount, error;
8028
8029     if (textColors[(int)cc].bg > 0) {
8030         if (textColors[(int)cc].fg > 0) {
8031             sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
8032                     textColors[(int)cc].fg, textColors[(int)cc].bg);
8033         } else {
8034             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
8035                     textColors[(int)cc].bg);
8036         }
8037     } else {
8038         if (textColors[(int)cc].fg > 0) {
8039             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
8040                     textColors[(int)cc].fg);
8041         } else {
8042             sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
8043         }
8044     }
8045     count = strlen(buf);
8046     outCount = OutputToProcess(NoProc, buf, count, &error);
8047     if (outCount < count) {
8048         DisplayFatalError(_("Error writing to display"), error, 1);
8049     }
8050
8051     if (continuation) return;
8052     switch (cc) {
8053     case ColorShout:
8054       PlaySound(appData.soundShout);
8055       break;
8056     case ColorSShout:
8057       PlaySound(appData.soundSShout);
8058       break;
8059     case ColorChannel1:
8060       PlaySound(appData.soundChannel1);
8061       break;
8062     case ColorChannel:
8063       PlaySound(appData.soundChannel);
8064       break;
8065     case ColorKibitz:
8066       PlaySound(appData.soundKibitz);
8067       break;
8068     case ColorTell:
8069       PlaySound(appData.soundTell);
8070       break;
8071     case ColorChallenge:
8072       PlaySound(appData.soundChallenge);
8073       break;
8074     case ColorRequest:
8075       PlaySound(appData.soundRequest);
8076       break;
8077     case ColorSeek:
8078       PlaySound(appData.soundSeek);
8079       break;
8080     case ColorNormal:
8081     case ColorNone:
8082     default:
8083       break;
8084     }
8085 }
8086
8087 char *UserName()
8088 {
8089     return getpwuid(getuid())->pw_name;
8090 }
8091
8092 static char *ExpandPathName(path)
8093      char *path;
8094 {
8095     static char static_buf[2000];
8096     char *d, *s, buf[2000];
8097     struct passwd *pwd;
8098
8099     s = path;
8100     d = static_buf;
8101
8102     while (*s && isspace(*s))
8103       ++s;
8104
8105     if (!*s) {
8106         *d = 0;
8107         return static_buf;
8108     }
8109
8110     if (*s == '~') {
8111         if (*(s+1) == '/') {
8112             strcpy(d, getpwuid(getuid())->pw_dir);
8113             strcat(d, s+1);
8114         }
8115         else {
8116             strcpy(buf, s+1);
8117             *strchr(buf, '/') = 0;
8118             pwd = getpwnam(buf);
8119             if (!pwd)
8120               {
8121                   fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
8122                           buf, path);
8123                   return NULL;
8124               }
8125             strcpy(d, pwd->pw_dir);
8126             strcat(d, strchr(s+1, '/'));
8127         }
8128     }
8129     else
8130       strcpy(d, s);
8131
8132     return static_buf;
8133 }
8134
8135 char *HostName()
8136 {
8137     static char host_name[MSG_SIZ];
8138
8139 #if HAVE_GETHOSTNAME
8140     gethostname(host_name, MSG_SIZ);
8141     return host_name;
8142 #else  /* not HAVE_GETHOSTNAME */
8143 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
8144     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
8145     return host_name;
8146 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
8147     return "localhost";
8148 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
8149 #endif /* not HAVE_GETHOSTNAME */
8150 }
8151
8152 XtIntervalId delayedEventTimerXID = 0;
8153 DelayedEventCallback delayedEventCallback = 0;
8154
8155 void
8156 FireDelayedEvent()
8157 {
8158     delayedEventTimerXID = 0;
8159     delayedEventCallback();
8160 }
8161
8162 void
8163 ScheduleDelayedEvent(cb, millisec)
8164      DelayedEventCallback cb; long millisec;
8165 {
8166     if(delayedEventTimerXID && delayedEventCallback == cb)
8167         // [HGM] alive: replace, rather than add or flush identical event
8168         XtRemoveTimeOut(delayedEventTimerXID);
8169     delayedEventCallback = cb;
8170     delayedEventTimerXID =
8171       XtAppAddTimeOut(appContext, millisec,
8172                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
8173 }
8174
8175 DelayedEventCallback
8176 GetDelayedEvent()
8177 {
8178   if (delayedEventTimerXID) {
8179     return delayedEventCallback;
8180   } else {
8181     return NULL;
8182   }
8183 }
8184
8185 void
8186 CancelDelayedEvent()
8187 {
8188   if (delayedEventTimerXID) {
8189     XtRemoveTimeOut(delayedEventTimerXID);
8190     delayedEventTimerXID = 0;
8191   }
8192 }
8193
8194 XtIntervalId loadGameTimerXID = 0;
8195
8196 int LoadGameTimerRunning()
8197 {
8198     return loadGameTimerXID != 0;
8199 }
8200
8201 int StopLoadGameTimer()
8202 {
8203     if (loadGameTimerXID != 0) {
8204         XtRemoveTimeOut(loadGameTimerXID);
8205         loadGameTimerXID = 0;
8206         return TRUE;
8207     } else {
8208         return FALSE;
8209     }
8210 }
8211
8212 void
8213 LoadGameTimerCallback(arg, id)
8214      XtPointer arg;
8215      XtIntervalId *id;
8216 {
8217     loadGameTimerXID = 0;
8218     AutoPlayGameLoop();
8219 }
8220
8221 void
8222 StartLoadGameTimer(millisec)
8223      long millisec;
8224 {
8225     loadGameTimerXID =
8226       XtAppAddTimeOut(appContext, millisec,
8227                       (XtTimerCallbackProc) LoadGameTimerCallback,
8228                       (XtPointer) 0);
8229 }
8230
8231 XtIntervalId analysisClockXID = 0;
8232
8233 void
8234 AnalysisClockCallback(arg, id)
8235      XtPointer arg;
8236      XtIntervalId *id;
8237 {
8238     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
8239          || appData.icsEngineAnalyze) { // [DM]
8240         AnalysisPeriodicEvent(0);
8241         StartAnalysisClock();
8242     }
8243 }
8244
8245 void
8246 StartAnalysisClock()
8247 {
8248     analysisClockXID =
8249       XtAppAddTimeOut(appContext, 2000,
8250                       (XtTimerCallbackProc) AnalysisClockCallback,
8251                       (XtPointer) 0);
8252 }
8253
8254 XtIntervalId clockTimerXID = 0;
8255
8256 int ClockTimerRunning()
8257 {
8258     return clockTimerXID != 0;
8259 }
8260
8261 int StopClockTimer()
8262 {
8263     if (clockTimerXID != 0) {
8264         XtRemoveTimeOut(clockTimerXID);
8265         clockTimerXID = 0;
8266         return TRUE;
8267     } else {
8268         return FALSE;
8269     }
8270 }
8271
8272 void
8273 ClockTimerCallback(arg, id)
8274      XtPointer arg;
8275      XtIntervalId *id;
8276 {
8277     clockTimerXID = 0;
8278     DecrementClocks();
8279 }
8280
8281 void
8282 StartClockTimer(millisec)
8283      long millisec;
8284 {
8285     clockTimerXID =
8286       XtAppAddTimeOut(appContext, millisec,
8287                       (XtTimerCallbackProc) ClockTimerCallback,
8288                       (XtPointer) 0);
8289 }
8290
8291 void
8292 DisplayTimerLabel(w, color, timer, highlight)
8293      Widget w;
8294      char *color;
8295      long timer;
8296      int highlight;
8297 {
8298     char buf[MSG_SIZ];
8299     Arg args[16];
8300
8301     /* check for low time warning */
8302     Pixel foregroundOrWarningColor = timerForegroundPixel;
8303
8304     if (timer > 0 &&
8305         appData.lowTimeWarning && 
8306         (timer / 1000) < appData.icsAlarmTime)
8307       foregroundOrWarningColor = lowTimeWarningColor;
8308
8309     if (appData.clockMode) {
8310         sprintf(buf, "%s: %s", color, TimeString(timer));
8311         XtSetArg(args[0], XtNlabel, buf);
8312     } else {
8313         sprintf(buf, "%s  ", color);
8314         XtSetArg(args[0], XtNlabel, buf);
8315     }
8316
8317     if (highlight) {
8318
8319         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
8320         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
8321     } else {
8322         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
8323         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
8324     }
8325
8326     XtSetValues(w, args, 3);
8327 }
8328
8329 void
8330 DisplayWhiteClock(timeRemaining, highlight)
8331      long timeRemaining;
8332      int highlight;
8333 {
8334     Arg args[16];
8335
8336     if(appData.noGUI) return;
8337     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
8338     if (highlight && iconPixmap == bIconPixmap) {
8339         iconPixmap = wIconPixmap;
8340         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
8341         XtSetValues(shellWidget, args, 1);
8342     }
8343 }
8344
8345 void
8346 DisplayBlackClock(timeRemaining, highlight)
8347      long timeRemaining;
8348      int highlight;
8349 {
8350     Arg args[16];
8351
8352     if(appData.noGUI) return;
8353     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
8354     if (highlight && iconPixmap == wIconPixmap) {
8355         iconPixmap = bIconPixmap;
8356         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
8357         XtSetValues(shellWidget, args, 1);
8358     }
8359 }
8360
8361 #define CPNone 0
8362 #define CPReal 1
8363 #define CPComm 2
8364 #define CPSock 3
8365 #define CPLoop 4
8366 typedef int CPKind;
8367
8368 typedef struct {
8369     CPKind kind;
8370     int pid;
8371     int fdTo, fdFrom;
8372 } ChildProc;
8373
8374
8375 int StartChildProcess(cmdLine, dir, pr)
8376      char *cmdLine;
8377      char *dir;
8378      ProcRef *pr;
8379 {
8380     char *argv[64], *p;
8381     int i, pid;
8382     int to_prog[2], from_prog[2];
8383     ChildProc *cp;
8384     char buf[MSG_SIZ];
8385
8386     if (appData.debugMode) {
8387         fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
8388     }
8389
8390     /* We do NOT feed the cmdLine to the shell; we just
8391        parse it into blank-separated arguments in the
8392        most simple-minded way possible.
8393        */
8394     i = 0;
8395     strcpy(buf, cmdLine);
8396     p = buf;
8397     for (;;) {
8398         argv[i++] = p;
8399         p = strchr(p, ' ');
8400         if (p == NULL) break;
8401         *p++ = NULLCHAR;
8402     }
8403     argv[i] = NULL;
8404
8405     SetUpChildIO(to_prog, from_prog);
8406
8407     if ((pid = fork()) == 0) {
8408         /* Child process */
8409         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
8410         close(to_prog[1]);     // first close the unused pipe ends
8411         close(from_prog[0]);
8412         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
8413         dup2(from_prog[1], 1);
8414         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
8415         close(from_prog[1]);                   // and closing again loses one of the pipes!
8416         if(fileno(stderr) >= 2) // better safe than sorry...
8417                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
8418
8419         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
8420             perror(dir);
8421             exit(1);
8422         }
8423
8424         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
8425
8426         execvp(argv[0], argv);
8427
8428         /* If we get here, exec failed */
8429         perror(argv[0]);
8430         exit(1);
8431     }
8432
8433     /* Parent process */
8434     close(to_prog[0]);
8435     close(from_prog[1]);
8436
8437     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8438     cp->kind = CPReal;
8439     cp->pid = pid;
8440     cp->fdFrom = from_prog[0];
8441     cp->fdTo = to_prog[1];
8442     *pr = (ProcRef) cp;
8443     return 0;
8444 }
8445
8446 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
8447 static RETSIGTYPE AlarmCallBack(int n)
8448 {
8449     return;
8450 }
8451
8452 void
8453 DestroyChildProcess(pr, signalType)
8454      ProcRef pr;
8455      int signalType;
8456 {
8457     ChildProc *cp = (ChildProc *) pr;
8458
8459     if (cp->kind != CPReal) return;
8460     cp->kind = CPNone;
8461     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
8462         signal(SIGALRM, AlarmCallBack);
8463         alarm(3);
8464         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
8465             kill(cp->pid, SIGKILL); // kill it forcefully
8466             wait((int *) 0);        // and wait again
8467         }
8468     } else {
8469         if (signalType) {
8470             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
8471         }
8472         /* Process is exiting either because of the kill or because of
8473            a quit command sent by the backend; either way, wait for it to die.
8474         */
8475         wait((int *) 0);
8476     }
8477     close(cp->fdFrom);
8478     close(cp->fdTo);
8479 }
8480
8481 void
8482 InterruptChildProcess(pr)
8483      ProcRef pr;
8484 {
8485     ChildProc *cp = (ChildProc *) pr;
8486
8487     if (cp->kind != CPReal) return;
8488     (void) kill(cp->pid, SIGINT); /* stop it thinking */
8489 }
8490
8491 int OpenTelnet(host, port, pr)
8492      char *host;
8493      char *port;
8494      ProcRef *pr;
8495 {
8496     char cmdLine[MSG_SIZ];
8497
8498     if (port[0] == NULLCHAR) {
8499       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
8500     } else {
8501       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
8502     }
8503     return StartChildProcess(cmdLine, "", pr);
8504 }
8505
8506 int OpenTCP(host, port, pr)
8507      char *host;
8508      char *port;
8509      ProcRef *pr;
8510 {
8511 #if OMIT_SOCKETS
8512     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
8513 #else  /* !OMIT_SOCKETS */
8514     int s;
8515     struct sockaddr_in sa;
8516     struct hostent     *hp;
8517     unsigned short uport;
8518     ChildProc *cp;
8519
8520     if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
8521         return errno;
8522     }
8523
8524     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
8525     sa.sin_family = AF_INET;
8526     sa.sin_addr.s_addr = INADDR_ANY;
8527     uport = (unsigned short) 0;
8528     sa.sin_port = htons(uport);
8529     if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
8530         return errno;
8531     }
8532
8533     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
8534     if (!(hp = gethostbyname(host))) {
8535         int b0, b1, b2, b3;
8536         if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
8537             hp = (struct hostent *) calloc(1, sizeof(struct hostent));
8538             hp->h_addrtype = AF_INET;
8539             hp->h_length = 4;
8540             hp->h_addr_list = (char **) calloc(2, sizeof(char *));
8541             hp->h_addr_list[0] = (char *) malloc(4);
8542             hp->h_addr_list[0][0] = b0;
8543             hp->h_addr_list[0][1] = b1;
8544             hp->h_addr_list[0][2] = b2;
8545             hp->h_addr_list[0][3] = b3;
8546         } else {
8547             return ENOENT;
8548         }
8549     }
8550     sa.sin_family = hp->h_addrtype;
8551     uport = (unsigned short) atoi(port);
8552     sa.sin_port = htons(uport);
8553     memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
8554
8555     if (connect(s, (struct sockaddr *) &sa,
8556                 sizeof(struct sockaddr_in)) < 0) {
8557         return errno;
8558     }
8559
8560     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8561     cp->kind = CPSock;
8562     cp->pid = 0;
8563     cp->fdFrom = s;
8564     cp->fdTo = s;
8565     *pr = (ProcRef) cp;
8566
8567 #endif /* !OMIT_SOCKETS */
8568
8569     return 0;
8570 }
8571
8572 int OpenCommPort(name, pr)
8573      char *name;
8574      ProcRef *pr;
8575 {
8576     int fd;
8577     ChildProc *cp;
8578
8579     fd = open(name, 2, 0);
8580     if (fd < 0) return errno;
8581
8582     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8583     cp->kind = CPComm;
8584     cp->pid = 0;
8585     cp->fdFrom = fd;
8586     cp->fdTo = fd;
8587     *pr = (ProcRef) cp;
8588
8589     return 0;
8590 }
8591
8592 int OpenLoopback(pr)
8593      ProcRef *pr;
8594 {
8595     ChildProc *cp;
8596     int to[2], from[2];
8597
8598     SetUpChildIO(to, from);
8599
8600     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8601     cp->kind = CPLoop;
8602     cp->pid = 0;
8603     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
8604     cp->fdTo = to[1];
8605     *pr = (ProcRef) cp;
8606
8607     return 0;
8608 }
8609
8610 int OpenRcmd(host, user, cmd, pr)
8611      char *host, *user, *cmd;
8612      ProcRef *pr;
8613 {
8614     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
8615     return -1;
8616 }
8617
8618 #define INPUT_SOURCE_BUF_SIZE 8192
8619
8620 typedef struct {
8621     CPKind kind;
8622     int fd;
8623     int lineByLine;
8624     char *unused;
8625     InputCallback func;
8626     XtInputId xid;
8627     char buf[INPUT_SOURCE_BUF_SIZE];
8628     VOIDSTAR closure;
8629 } InputSource;
8630
8631 void
8632 DoInputCallback(closure, source, xid)
8633      caddr_t closure;
8634      int *source;
8635      XtInputId *xid;
8636 {
8637     InputSource *is = (InputSource *) closure;
8638     int count;
8639     int error;
8640     char *p, *q;
8641
8642     if (is->lineByLine) {
8643         count = read(is->fd, is->unused,
8644                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
8645         if (count <= 0) {
8646             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
8647             return;
8648         }
8649         is->unused += count;
8650         p = is->buf;
8651         while (p < is->unused) {
8652             q = memchr(p, '\n', is->unused - p);
8653             if (q == NULL) break;
8654             q++;
8655             (is->func)(is, is->closure, p, q - p, 0);
8656             p = q;
8657         }
8658         q = is->buf;
8659         while (p < is->unused) {
8660             *q++ = *p++;
8661         }
8662         is->unused = q;
8663     } else {
8664         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
8665         if (count == -1)
8666           error = errno;
8667         else
8668           error = 0;
8669         (is->func)(is, is->closure, is->buf, count, error);
8670     }
8671 }
8672
8673 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
8674      ProcRef pr;
8675      int lineByLine;
8676      InputCallback func;
8677      VOIDSTAR closure;
8678 {
8679     InputSource *is;
8680     ChildProc *cp = (ChildProc *) pr;
8681
8682     is = (InputSource *) calloc(1, sizeof(InputSource));
8683     is->lineByLine = lineByLine;
8684     is->func = func;
8685     if (pr == NoProc) {
8686         is->kind = CPReal;
8687         is->fd = fileno(stdin);
8688     } else {
8689         is->kind = cp->kind;
8690         is->fd = cp->fdFrom;
8691     }
8692     if (lineByLine) {
8693         is->unused = is->buf;
8694     }
8695
8696     is->xid = XtAppAddInput(appContext, is->fd,
8697                             (XtPointer) (XtInputReadMask),
8698                             (XtInputCallbackProc) DoInputCallback,
8699                             (XtPointer) is);
8700     is->closure = closure;
8701     return (InputSourceRef) is;
8702 }
8703
8704 void
8705 RemoveInputSource(isr)
8706      InputSourceRef isr;
8707 {
8708     InputSource *is = (InputSource *) isr;
8709
8710     if (is->xid == 0) return;
8711     XtRemoveInput(is->xid);
8712     is->xid = 0;
8713 }
8714
8715 int OutputToProcess(pr, message, count, outError)
8716      ProcRef pr;
8717      char *message;
8718      int count;
8719      int *outError;
8720 {
8721     ChildProc *cp = (ChildProc *) pr;
8722     int outCount;
8723
8724     if (pr == NoProc)
8725       outCount = fwrite(message, 1, count, stdout);
8726     else
8727       outCount = write(cp->fdTo, message, count);
8728
8729     if (outCount == -1)
8730       *outError = errno;
8731     else
8732       *outError = 0;
8733
8734     return outCount;
8735 }
8736
8737 /* Output message to process, with "ms" milliseconds of delay
8738    between each character. This is needed when sending the logon
8739    script to ICC, which for some reason doesn't like the
8740    instantaneous send. */
8741 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8742      ProcRef pr;
8743      char *message;
8744      int count;
8745      int *outError;
8746      long msdelay;
8747 {
8748     ChildProc *cp = (ChildProc *) pr;
8749     int outCount = 0;
8750     int r;
8751
8752     while (count--) {
8753         r = write(cp->fdTo, message++, 1);
8754         if (r == -1) {
8755             *outError = errno;
8756             return outCount;
8757         }
8758         ++outCount;
8759         if (msdelay >= 0)
8760           TimeDelay(msdelay);
8761     }
8762
8763     return outCount;
8764 }
8765
8766 /****   Animation code by Hugh Fisher, DCS, ANU.
8767
8768         Known problem: if a window overlapping the board is
8769         moved away while a piece is being animated underneath,
8770         the newly exposed area won't be updated properly.
8771         I can live with this.
8772
8773         Known problem: if you look carefully at the animation
8774         of pieces in mono mode, they are being drawn as solid
8775         shapes without interior detail while moving. Fixing
8776         this would be a major complication for minimal return.
8777 ****/
8778
8779 /*      Masks for XPM pieces. Black and white pieces can have
8780         different shapes, but in the interest of retaining my
8781         sanity pieces must have the same outline on both light
8782         and dark squares, and all pieces must use the same
8783         background square colors/images.                */
8784
8785 static int xpmDone = 0;
8786
8787 static void
8788 CreateAnimMasks (pieceDepth)
8789      int pieceDepth;
8790 {
8791   ChessSquare   piece;
8792   Pixmap        buf;
8793   GC            bufGC, maskGC;
8794   int           kind, n;
8795   unsigned long plane;
8796   XGCValues     values;
8797
8798   /* Need a bitmap just to get a GC with right depth */
8799   buf = XCreatePixmap(xDisplay, xBoardWindow,
8800                         8, 8, 1);
8801   values.foreground = 1;
8802   values.background = 0;
8803   /* Don't use XtGetGC, not read only */
8804   maskGC = XCreateGC(xDisplay, buf,
8805                     GCForeground | GCBackground, &values);
8806   XFreePixmap(xDisplay, buf);
8807
8808   buf = XCreatePixmap(xDisplay, xBoardWindow,
8809                       squareSize, squareSize, pieceDepth);
8810   values.foreground = XBlackPixel(xDisplay, xScreen);
8811   values.background = XWhitePixel(xDisplay, xScreen);
8812   bufGC = XCreateGC(xDisplay, buf,
8813                     GCForeground | GCBackground, &values);
8814
8815   for (piece = WhitePawn; piece <= BlackKing; piece++) {
8816     /* Begin with empty mask */
8817     if(!xpmDone) // [HGM] pieces: keep using existing
8818     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8819                                  squareSize, squareSize, 1);
8820     XSetFunction(xDisplay, maskGC, GXclear);
8821     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8822                    0, 0, squareSize, squareSize);
8823
8824     /* Take a copy of the piece */
8825     if (White(piece))
8826       kind = 0;
8827     else
8828       kind = 2;
8829     XSetFunction(xDisplay, bufGC, GXcopy);
8830     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8831               buf, bufGC,
8832               0, 0, squareSize, squareSize, 0, 0);
8833
8834     /* XOR the background (light) over the piece */
8835     XSetFunction(xDisplay, bufGC, GXxor);
8836     if (useImageSqs)
8837       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8838                 0, 0, squareSize, squareSize, 0, 0);
8839     else {
8840       XSetForeground(xDisplay, bufGC, lightSquareColor);
8841       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8842     }
8843
8844     /* We now have an inverted piece image with the background
8845        erased. Construct mask by just selecting all the non-zero
8846        pixels - no need to reconstruct the original image.      */
8847     XSetFunction(xDisplay, maskGC, GXor);
8848     plane = 1;
8849     /* Might be quicker to download an XImage and create bitmap
8850        data from it rather than this N copies per piece, but it
8851        only takes a fraction of a second and there is a much
8852        longer delay for loading the pieces.             */
8853     for (n = 0; n < pieceDepth; n ++) {
8854       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8855                  0, 0, squareSize, squareSize,
8856                  0, 0, plane);
8857       plane = plane << 1;
8858     }
8859   }
8860   /* Clean up */
8861   XFreePixmap(xDisplay, buf);
8862   XFreeGC(xDisplay, bufGC);
8863   XFreeGC(xDisplay, maskGC);
8864 }
8865
8866 static void
8867 InitAnimState (anim, info)
8868   AnimState * anim;
8869   XWindowAttributes * info;
8870 {
8871   XtGCMask  mask;
8872   XGCValues values;
8873
8874   /* Each buffer is square size, same depth as window */
8875   anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8876                         squareSize, squareSize, info->depth);
8877   anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8878                         squareSize, squareSize, info->depth);
8879
8880   /* Create a plain GC for blitting */
8881   mask = GCForeground | GCBackground | GCFunction |
8882          GCPlaneMask | GCGraphicsExposures;
8883   values.foreground = XBlackPixel(xDisplay, xScreen);
8884   values.background = XWhitePixel(xDisplay, xScreen);
8885   values.function   = GXcopy;
8886   values.plane_mask = AllPlanes;
8887   values.graphics_exposures = False;
8888   anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8889
8890   /* Piece will be copied from an existing context at
8891      the start of each new animation/drag. */
8892   anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8893
8894   /* Outline will be a read-only copy of an existing */
8895   anim->outlineGC = None;
8896 }
8897
8898 static void
8899 CreateAnimVars ()
8900 {
8901   static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
8902   XWindowAttributes info;
8903
8904   if (xpmDone && gameInfo.variant == old) return;
8905   if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
8906   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8907
8908   InitAnimState(&game, &info);
8909   InitAnimState(&player, &info);
8910
8911   /* For XPM pieces, we need bitmaps to use as masks. */
8912   if (useImages)
8913     CreateAnimMasks(info.depth);
8914    xpmDone = 1;
8915 }
8916
8917 #ifndef HAVE_USLEEP
8918
8919 static Boolean frameWaiting;
8920
8921 static RETSIGTYPE FrameAlarm (sig)
8922      int sig;
8923 {
8924   frameWaiting = False;
8925   /* In case System-V style signals.  Needed?? */
8926   signal(SIGALRM, FrameAlarm);
8927 }
8928
8929 static void
8930 FrameDelay (time)
8931      int time;
8932 {
8933   struct itimerval delay;
8934
8935   XSync(xDisplay, False);
8936
8937   if (time > 0) {
8938     frameWaiting = True;
8939     signal(SIGALRM, FrameAlarm);
8940     delay.it_interval.tv_sec =
8941       delay.it_value.tv_sec = time / 1000;
8942     delay.it_interval.tv_usec =
8943       delay.it_value.tv_usec = (time % 1000) * 1000;
8944     setitimer(ITIMER_REAL, &delay, NULL);
8945 #if 0
8946     /* Ugh -- busy-wait! --tpm */
8947     while (frameWaiting);
8948 #else
8949     while (frameWaiting) pause();
8950 #endif
8951     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8952     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8953     setitimer(ITIMER_REAL, &delay, NULL);
8954   }
8955 }
8956
8957 #else
8958
8959 static void
8960 FrameDelay (time)
8961      int time;
8962 {
8963   XSync(xDisplay, False);
8964   if (time > 0)
8965     usleep(time * 1000);
8966 }
8967
8968 #endif
8969
8970 /*      Convert board position to corner of screen rect and color       */
8971
8972 static void
8973 ScreenSquare(column, row, pt, color)
8974      int column; int row; XPoint * pt; int * color;
8975 {
8976   if (flipView) {
8977     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8978     pt->y = lineGap + row * (squareSize + lineGap);
8979   } else {
8980     pt->x = lineGap + column * (squareSize + lineGap);
8981     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8982   }
8983   *color = SquareColor(row, column);
8984 }
8985
8986 /*      Convert window coords to square                 */
8987
8988 static void
8989 BoardSquare(x, y, column, row)
8990      int x; int y; int * column; int * row;
8991 {
8992   *column = EventToSquare(x, BOARD_WIDTH);
8993   if (flipView && *column >= 0)
8994     *column = BOARD_WIDTH - 1 - *column;
8995   *row = EventToSquare(y, BOARD_HEIGHT);
8996   if (!flipView && *row >= 0)
8997     *row = BOARD_HEIGHT - 1 - *row;
8998 }
8999
9000 /*   Utilities  */
9001
9002 #undef Max  /* just in case */
9003 #undef Min
9004 #define Max(a, b) ((a) > (b) ? (a) : (b))
9005 #define Min(a, b) ((a) < (b) ? (a) : (b))
9006
9007 static void
9008 SetRect(rect, x, y, width, height)
9009      XRectangle * rect; int x; int y; int width; int height;
9010 {
9011   rect->x = x;
9012   rect->y = y;
9013   rect->width  = width;
9014   rect->height = height;
9015 }
9016
9017 /*      Test if two frames overlap. If they do, return
9018         intersection rect within old and location of
9019         that rect within new. */
9020
9021 static Boolean
9022 Intersect(old, new, size, area, pt)
9023      XPoint * old; XPoint * new;
9024      int size; XRectangle * area; XPoint * pt;
9025 {
9026   if (old->x > new->x + size || new->x > old->x + size ||
9027       old->y > new->y + size || new->y > old->y + size) {
9028     return False;
9029   } else {
9030     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
9031             size - abs(old->x - new->x), size - abs(old->y - new->y));
9032     pt->x = Max(old->x - new->x, 0);
9033     pt->y = Max(old->y - new->y, 0);
9034     return True;
9035   }
9036 }
9037
9038 /*      For two overlapping frames, return the rect(s)
9039         in the old that do not intersect with the new.   */
9040
9041 static void
9042 CalcUpdateRects(old, new, size, update, nUpdates)
9043      XPoint * old; XPoint * new; int size;
9044      XRectangle update[]; int * nUpdates;
9045 {
9046   int        count;
9047
9048   /* If old = new (shouldn't happen) then nothing to draw */
9049   if (old->x == new->x && old->y == new->y) {
9050     *nUpdates = 0;
9051     return;
9052   }
9053   /* Work out what bits overlap. Since we know the rects
9054      are the same size we don't need a full intersect calc. */
9055   count = 0;
9056   /* Top or bottom edge? */
9057   if (new->y > old->y) {
9058     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
9059     count ++;
9060   } else if (old->y > new->y) {
9061     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
9062                               size, old->y - new->y);
9063     count ++;
9064   }
9065   /* Left or right edge - don't overlap any update calculated above. */
9066   if (new->x > old->x) {
9067     SetRect(&(update[count]), old->x, Max(new->y, old->y),
9068                               new->x - old->x, size - abs(new->y - old->y));
9069     count ++;
9070   } else if (old->x > new->x) {
9071     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
9072                               old->x - new->x, size - abs(new->y - old->y));
9073     count ++;
9074   }
9075   /* Done */
9076   *nUpdates = count;
9077 }
9078
9079 /*      Generate a series of frame coords from start->mid->finish.
9080         The movement rate doubles until the half way point is
9081         reached, then halves back down to the final destination,
9082         which gives a nice slow in/out effect. The algorithmn
9083         may seem to generate too many intermediates for short
9084         moves, but remember that the purpose is to attract the
9085         viewers attention to the piece about to be moved and
9086         then to where it ends up. Too few frames would be less
9087         noticeable.                                             */
9088
9089 static void
9090 Tween(start, mid, finish, factor, frames, nFrames)
9091      XPoint * start; XPoint * mid;
9092      XPoint * finish; int factor;
9093      XPoint frames[]; int * nFrames;
9094 {
9095   int fraction, n, count;
9096
9097   count = 0;
9098
9099   /* Slow in, stepping 1/16th, then 1/8th, ... */
9100   fraction = 1;
9101   for (n = 0; n < factor; n++)
9102     fraction *= 2;
9103   for (n = 0; n < factor; n++) {
9104     frames[count].x = start->x + (mid->x - start->x) / fraction;
9105     frames[count].y = start->y + (mid->y - start->y) / fraction;
9106     count ++;
9107     fraction = fraction / 2;
9108   }
9109
9110   /* Midpoint */
9111   frames[count] = *mid;
9112   count ++;
9113
9114   /* Slow out, stepping 1/2, then 1/4, ... */
9115   fraction = 2;
9116   for (n = 0; n < factor; n++) {
9117     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
9118     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
9119     count ++;
9120     fraction = fraction * 2;
9121   }
9122   *nFrames = count;
9123 }
9124
9125 /*      Draw a piece on the screen without disturbing what's there      */
9126
9127 static void
9128 SelectGCMask(piece, clip, outline, mask)
9129      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
9130 {
9131   GC source;
9132
9133   /* Bitmap for piece being moved. */
9134   if (appData.monoMode) {
9135       *mask = *pieceToSolid(piece);
9136   } else if (useImages) {
9137 #if HAVE_LIBXPM
9138       *mask = xpmMask[piece];
9139 #else
9140       *mask = ximMaskPm[piece];
9141 #endif
9142   } else {
9143       *mask = *pieceToSolid(piece);
9144   }
9145
9146   /* GC for piece being moved. Square color doesn't matter, but
9147      since it gets modified we make a copy of the original. */
9148   if (White(piece)) {
9149     if (appData.monoMode)
9150       source = bwPieceGC;
9151     else
9152       source = wlPieceGC;
9153   } else {
9154     if (appData.monoMode)
9155       source = wbPieceGC;
9156     else
9157       source = blPieceGC;
9158   }
9159   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
9160
9161   /* Outline only used in mono mode and is not modified */
9162   if (White(piece))
9163     *outline = bwPieceGC;
9164   else
9165     *outline = wbPieceGC;
9166 }
9167
9168 static void
9169 OverlayPiece(piece, clip, outline,  dest)
9170      ChessSquare piece; GC clip; GC outline; Drawable dest;
9171 {
9172   int   kind;
9173
9174   if (!useImages) {
9175     /* Draw solid rectangle which will be clipped to shape of piece */
9176     XFillRectangle(xDisplay, dest, clip,
9177                    0, 0, squareSize, squareSize);
9178     if (appData.monoMode)
9179       /* Also draw outline in contrasting color for black
9180          on black / white on white cases                */
9181       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
9182                  0, 0, squareSize, squareSize, 0, 0, 1);
9183   } else {
9184     /* Copy the piece */
9185     if (White(piece))
9186       kind = 0;
9187     else
9188       kind = 2;
9189     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
9190               dest, clip,
9191               0, 0, squareSize, squareSize,
9192               0, 0);
9193   }
9194 }
9195
9196 /* Animate the movement of a single piece */
9197
9198 static void
9199 BeginAnimation(anim, piece, startColor, start)
9200      AnimState *anim;
9201      ChessSquare piece;
9202      int startColor;
9203      XPoint * start;
9204 {
9205   Pixmap mask;
9206
9207   /* The old buffer is initialised with the start square (empty) */
9208   BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
9209   anim->prevFrame = *start;
9210
9211   /* The piece will be drawn using its own bitmap as a matte    */
9212   SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
9213   XSetClipMask(xDisplay, anim->pieceGC, mask);
9214 }
9215
9216 static void
9217 AnimationFrame(anim, frame, piece)
9218      AnimState *anim;
9219      XPoint *frame;
9220      ChessSquare piece;
9221 {
9222   XRectangle updates[4];
9223   XRectangle overlap;
9224   XPoint     pt;
9225   int        count, i;
9226
9227   /* Save what we are about to draw into the new buffer */
9228   XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
9229             frame->x, frame->y, squareSize, squareSize,
9230             0, 0);
9231
9232   /* Erase bits of the previous frame */
9233   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
9234     /* Where the new frame overlapped the previous,
9235        the contents in newBuf are wrong. */
9236     XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
9237               overlap.x, overlap.y,
9238               overlap.width, overlap.height,
9239               pt.x, pt.y);
9240     /* Repaint the areas in the old that don't overlap new */
9241     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
9242     for (i = 0; i < count; i++)
9243       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
9244                 updates[i].x - anim->prevFrame.x,
9245                 updates[i].y - anim->prevFrame.y,
9246                 updates[i].width, updates[i].height,
9247                 updates[i].x, updates[i].y);
9248   } else {
9249     /* Easy when no overlap */
9250     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
9251                   0, 0, squareSize, squareSize,
9252                   anim->prevFrame.x, anim->prevFrame.y);
9253   }
9254
9255   /* Save this frame for next time round */
9256   XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
9257                 0, 0, squareSize, squareSize,
9258                 0, 0);
9259   anim->prevFrame = *frame;
9260
9261   /* Draw piece over original screen contents, not current,
9262      and copy entire rect. Wipes out overlapping piece images. */
9263   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
9264   XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
9265                 0, 0, squareSize, squareSize,
9266                 frame->x, frame->y);
9267 }
9268
9269 static void
9270 EndAnimation (anim, finish)
9271      AnimState *anim;
9272      XPoint *finish;
9273 {
9274   XRectangle updates[4];
9275   XRectangle overlap;
9276   XPoint     pt;
9277   int        count, i;
9278
9279   /* The main code will redraw the final square, so we
9280      only need to erase the bits that don't overlap.    */
9281   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
9282     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
9283     for (i = 0; i < count; i++)
9284       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
9285                 updates[i].x - anim->prevFrame.x,
9286                 updates[i].y - anim->prevFrame.y,
9287                 updates[i].width, updates[i].height,
9288                 updates[i].x, updates[i].y);
9289   } else {
9290     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
9291                 0, 0, squareSize, squareSize,
9292                 anim->prevFrame.x, anim->prevFrame.y);
9293   }
9294 }
9295
9296 static void
9297 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
9298      AnimState *anim;
9299      ChessSquare piece; int startColor;
9300      XPoint * start; XPoint * finish;
9301      XPoint frames[]; int nFrames;
9302 {
9303   int n;
9304
9305   BeginAnimation(anim, piece, startColor, start);
9306   for (n = 0; n < nFrames; n++) {
9307     AnimationFrame(anim, &(frames[n]), piece);
9308     FrameDelay(appData.animSpeed);
9309   }
9310   EndAnimation(anim, finish);
9311 }
9312
9313 /* Main control logic for deciding what to animate and how */
9314
9315 void
9316 AnimateMove(board, fromX, fromY, toX, toY)
9317      Board board;
9318      int fromX;
9319      int fromY;
9320      int toX;
9321      int toY;
9322 {
9323   ChessSquare piece;
9324   int hop;
9325   XPoint      start, finish, mid;
9326   XPoint      frames[kFactor * 2 + 1];
9327   int         nFrames, startColor, endColor;
9328
9329   /* Are we animating? */
9330   if (!appData.animate || appData.blindfold)
9331     return;
9332
9333   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing || 
9334      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing) 
9335         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
9336
9337   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
9338   piece = board[fromY][fromX];
9339   if (piece >= EmptySquare) return;
9340
9341 #if DONT_HOP
9342   hop = FALSE;
9343 #else
9344   hop = (piece == WhiteKnight || piece == BlackKnight);
9345 #endif
9346
9347   if (appData.debugMode) {
9348       fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
9349                              _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
9350              piece, fromX, fromY, toX, toY);  }
9351
9352   ScreenSquare(fromX, fromY, &start, &startColor);
9353   ScreenSquare(toX, toY, &finish, &endColor);
9354
9355   if (hop) {
9356     /* Knight: make diagonal movement then straight */
9357     if (abs(toY - fromY) < abs(toX - fromX)) {
9358        mid.x = start.x + (finish.x - start.x) / 2;
9359        mid.y = finish.y;
9360      } else {
9361        mid.x = finish.x;
9362        mid.y = start.y + (finish.y - start.y) / 2;
9363      }
9364   } else {
9365     mid.x = start.x + (finish.x - start.x) / 2;
9366     mid.y = start.y + (finish.y - start.y) / 2;
9367   }
9368
9369   /* Don't use as many frames for very short moves */
9370   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
9371     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
9372   else
9373     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
9374   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
9375
9376   /* Be sure end square is redrawn */
9377   damage[toY][toX] = True;
9378 }
9379
9380 static void
9381 DragPieceBegin(x, y)
9382      int x; int y;
9383 {
9384     int  boardX, boardY, color;
9385     XPoint corner;
9386
9387     /* Are we animating? */
9388     if (!appData.animateDragging || appData.blindfold)
9389       return;
9390
9391     /* Figure out which square we start in and the
9392        mouse position relative to top left corner. */
9393     BoardSquare(x, y, &boardX, &boardY);
9394     player.startBoardX = boardX;
9395     player.startBoardY = boardY;
9396     ScreenSquare(boardX, boardY, &corner, &color);
9397     player.startSquare  = corner;
9398     player.startColor   = color;
9399 #if 0
9400     /* Start from exactly where the piece is.  This can be confusing
9401        if you start dragging far from the center of the square; most
9402        or all of the piece can be over a different square from the one
9403        the mouse pointer is in. */
9404     player.mouseDelta.x = x - corner.x;
9405     player.mouseDelta.y = y - corner.y;
9406 #else
9407     /* As soon as we start dragging, the piece will jump slightly to
9408        be centered over the mouse pointer. */
9409     player.mouseDelta.x = squareSize/2;
9410     player.mouseDelta.y = squareSize/2;
9411 #endif
9412     /* Initialise animation */
9413     player.dragPiece = PieceForSquare(boardX, boardY);
9414     /* Sanity check */
9415     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
9416         player.dragActive = True;
9417         BeginAnimation(&player, player.dragPiece, color, &corner);
9418         /* Mark this square as needing to be redrawn. Note that
9419            we don't remove the piece though, since logically (ie
9420            as seen by opponent) the move hasn't been made yet. */
9421            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
9422               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
9423            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
9424                      corner.x, corner.y, squareSize, squareSize,
9425                      0, 0); // [HGM] zh: unstack in stead of grab
9426         damage[boardY][boardX] = True;
9427     } else {
9428         player.dragActive = False;
9429     }
9430 }
9431
9432 static void
9433 DragPieceMove(x, y)
9434      int x; int y;
9435 {
9436     XPoint corner;
9437
9438     /* Are we animating? */
9439     if (!appData.animateDragging || appData.blindfold)
9440       return;
9441
9442     /* Sanity check */
9443     if (! player.dragActive)
9444       return;
9445     /* Move piece, maintaining same relative position
9446        of mouse within square    */
9447     corner.x = x - player.mouseDelta.x;
9448     corner.y = y - player.mouseDelta.y;
9449     AnimationFrame(&player, &corner, player.dragPiece);
9450 #if HIGHDRAG
9451     if (appData.highlightDragging) {
9452         int boardX, boardY;
9453         BoardSquare(x, y, &boardX, &boardY);
9454         SetHighlights(fromX, fromY, boardX, boardY);
9455     }
9456 #endif
9457 }
9458
9459 static void
9460 DragPieceEnd(x, y)
9461      int x; int y;
9462 {
9463     int boardX, boardY, color;
9464     XPoint corner;
9465
9466     /* Are we animating? */
9467     if (!appData.animateDragging || appData.blindfold)
9468       return;
9469
9470     /* Sanity check */
9471     if (! player.dragActive)
9472       return;
9473     /* Last frame in sequence is square piece is
9474        placed on, which may not match mouse exactly. */
9475     BoardSquare(x, y, &boardX, &boardY);
9476     ScreenSquare(boardX, boardY, &corner, &color);
9477     EndAnimation(&player, &corner);
9478
9479     /* Be sure end square is redrawn */
9480     damage[boardY][boardX] = True;
9481
9482     /* This prevents weird things happening with fast successive
9483        clicks which on my Sun at least can cause motion events
9484        without corresponding press/release. */
9485     player.dragActive = False;
9486 }
9487
9488 /* Handle expose event while piece being dragged */
9489
9490 static void
9491 DrawDragPiece ()
9492 {
9493   if (!player.dragActive || appData.blindfold)
9494     return;
9495
9496   /* What we're doing: logically, the move hasn't been made yet,
9497      so the piece is still in it's original square. But visually
9498      it's being dragged around the board. So we erase the square
9499      that the piece is on and draw it at the last known drag point. */
9500   BlankSquare(player.startSquare.x, player.startSquare.y,
9501                 player.startColor, EmptySquare, xBoardWindow);
9502   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
9503   damage[player.startBoardY][player.startBoardX] = TRUE;
9504 }
9505
9506 void
9507 SetProgramStats( FrontEndProgramStats * stats )
9508 {
9509   // [HR] TODO
9510   // [HGM] done, but perhaps backend should call this directly?
9511     EngineOutputUpdate( stats );
9512 }