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