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