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