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