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