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