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