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