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