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