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