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