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