Fix castling rights when copying FEN to clipboard (again)
[xboard.git] / xboard.c
1 /*
2  * xboard.c -- X front end for XBoard
3  *
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,
5  * Massachusetts. 
6  *
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8  * 2007, 2008, 2009 Free Software Foundation, Inc.
9  *
10  * The following terms apply to Digital Equipment Corporation's copyright
11  * interest in XBoard:
12  * ------------------------------------------------------------------------
13  * All Rights Reserved
14  *
15  * Permission to use, copy, modify, and distribute this software and its
16  * documentation for any purpose and without fee is hereby granted,
17  * provided that the above copyright notice appear in all copies and that
18  * both that copyright notice and this permission notice appear in
19  * supporting documentation, and that the name of Digital not be
20  * used in advertising or publicity pertaining to distribution of the
21  * software without specific, written prior permission.
22  *
23  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29  * SOFTWARE.
30  * ------------------------------------------------------------------------
31  *
32  * The following terms apply to the enhanced version of XBoard
33  * distributed by the Free Software Foundation:
34  * ------------------------------------------------------------------------
35  *
36  * GNU XBoard is free software: you can redistribute it and/or modify
37  * it under the terms of the GNU General Public License as published by
38  * the Free Software Foundation, either version 3 of the License, or (at
39  * your option) any later version.
40  *
41  * GNU XBoard is distributed in the hope that it will be useful, but
42  * WITHOUT ANY WARRANTY; without even the implied warranty of
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44  * General Public License for more details.
45  *
46  * You should have received a copy of the GNU General Public License
47  * along with this program. If not, see http://www.gnu.org/licenses/.  *
48  *
49  *------------------------------------------------------------------------
50  ** See the file ChangeLog for a revision history.  */
51
52 #include "config.h"
53
54 #include <stdio.h>
55 #include <ctype.h>
56 #include <signal.h>
57 #include <errno.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <pwd.h>
61
62 #if !OMIT_SOCKETS
63 # if HAVE_SYS_SOCKET_H
64 #  include <sys/socket.h>
65 #  include <netinet/in.h>
66 #  include <netdb.h>
67 # else /* not HAVE_SYS_SOCKET_H */
68 #  if HAVE_LAN_SOCKET_H
69 #   include <lan/socket.h>
70 #   include <lan/in.h>
71 #   include <lan/netdb.h>
72 #  else /* not HAVE_LAN_SOCKET_H */
73 #   define OMIT_SOCKETS 1
74 #  endif /* not HAVE_LAN_SOCKET_H */
75 # endif /* not HAVE_SYS_SOCKET_H */
76 #endif /* !OMIT_SOCKETS */
77
78 #if STDC_HEADERS
79 # include <stdlib.h>
80 # include <string.h>
81 #else /* not STDC_HEADERS */
82 extern char *getenv();
83 # if HAVE_STRING_H
84 #  include <string.h>
85 # else /* not HAVE_STRING_H */
86 #  include <strings.h>
87 # endif /* not HAVE_STRING_H */
88 #endif /* not STDC_HEADERS */
89
90 #if HAVE_SYS_FCNTL_H
91 # include <sys/fcntl.h>
92 #else /* not HAVE_SYS_FCNTL_H */
93 # if HAVE_FCNTL_H
94 #  include <fcntl.h>
95 # endif /* HAVE_FCNTL_H */
96 #endif /* not HAVE_SYS_FCNTL_H */
97
98 #if HAVE_SYS_SYSTEMINFO_H
99 # include <sys/systeminfo.h>
100 #endif /* HAVE_SYS_SYSTEMINFO_H */
101
102 #if TIME_WITH_SYS_TIME
103 # include <sys/time.h>
104 # include <time.h>
105 #else
106 # if HAVE_SYS_TIME_H
107 #  include <sys/time.h>
108 # else
109 #  include <time.h>
110 # endif
111 #endif
112
113 #if HAVE_UNISTD_H
114 # include <unistd.h>
115 #endif
116
117 #if HAVE_SYS_WAIT_H
118 # include <sys/wait.h>
119 #endif
120
121 #if HAVE_DIRENT_H
122 # include <dirent.h>
123 # define NAMLEN(dirent) strlen((dirent)->d_name)
124 # define HAVE_DIR_STRUCT
125 #else
126 # define dirent direct
127 # define NAMLEN(dirent) (dirent)->d_namlen
128 # if HAVE_SYS_NDIR_H
129 #  include <sys/ndir.h>
130 #  define HAVE_DIR_STRUCT
131 # endif
132 # if HAVE_SYS_DIR_H
133 #  include <sys/dir.h>
134 #  define HAVE_DIR_STRUCT
135 # endif
136 # if HAVE_NDIR_H
137 #  include <ndir.h>
138 #  define HAVE_DIR_STRUCT
139 # endif
140 #endif
141
142 #include <X11/Intrinsic.h>
143 #include <X11/StringDefs.h>
144 #include <X11/Shell.h>
145 #include <X11/cursorfont.h>
146 #include <X11/Xatom.h>
147 #include <X11/Xmu/Atoms.h>
148 #if USE_XAW3D
149 #include <X11/Xaw3d/Dialog.h>
150 #include <X11/Xaw3d/Form.h>
151 #include <X11/Xaw3d/List.h>
152 #include <X11/Xaw3d/Label.h>
153 #include <X11/Xaw3d/SimpleMenu.h>
154 #include <X11/Xaw3d/SmeBSB.h>
155 #include <X11/Xaw3d/SmeLine.h>
156 #include <X11/Xaw3d/Box.h>
157 #include <X11/Xaw3d/MenuButton.h>
158 #include <X11/Xaw3d/Text.h>
159 #include <X11/Xaw3d/AsciiText.h>
160 #else
161 #include <X11/Xaw/Dialog.h>
162 #include <X11/Xaw/Form.h>
163 #include <X11/Xaw/List.h>
164 #include <X11/Xaw/Label.h>
165 #include <X11/Xaw/SimpleMenu.h>
166 #include <X11/Xaw/SmeBSB.h>
167 #include <X11/Xaw/SmeLine.h>
168 #include <X11/Xaw/Box.h>
169 #include <X11/Xaw/MenuButton.h>
170 #include <X11/Xaw/Text.h>
171 #include <X11/Xaw/AsciiText.h>
172 #endif
173
174 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
175 #include "common.h"
176
177 #if HAVE_LIBXPM
178 #include <X11/xpm.h>
179 #include "pixmaps/pixmaps.h"
180 #define IMAGE_EXT "xpm"
181 #else
182 #define IMAGE_EXT "xim"
183 #include "bitmaps/bitmaps.h"
184 #endif
185
186 #include "bitmaps/icon_white.bm"
187 #include "bitmaps/icon_black.bm"
188 #include "bitmaps/checkmark.bm"
189
190 #include "frontend.h"
191 #include "backend.h"
192 #include "moves.h"
193 #include "xboard.h"
194 #include "childio.h"
195 #include "xgamelist.h"
196 #include "xhistory.h"
197 #include "xedittags.h"
198 #include "gettext.h"
199
200 // must be moved to xengineoutput.h
201
202 void EngineOutputProc P((Widget w, XEvent *event,
203  String *prms, Cardinal *nprms));
204
205 void EngineOutputPopDown();
206
207
208 #ifdef __EMX__
209 #ifndef HAVE_USLEEP
210 #define HAVE_USLEEP
211 #endif
212 #define usleep(t)   _sleep2(((t)+500)/1000)
213 #endif
214
215 #ifdef ENABLE_NLS
216 # define  _(s) gettext (s)
217 # define N_(s) gettext_noop (s)
218 #else
219 # define  _(s) (s)
220 # define N_(s)  s
221 #endif
222
223 typedef struct {
224     String string;
225     XtActionProc proc;
226 } MenuItem;
227
228 typedef struct {
229     String name;
230     MenuItem *mi;
231 } Menu;
232
233 int main P((int argc, char **argv));
234 RETSIGTYPE CmailSigHandler P((int sig));
235 RETSIGTYPE IntSigHandler P((int sig));
236 RETSIGTYPE TermSizeSigHandler P((int sig));
237 void CreateGCs P((void));
238 void CreateXIMPieces P((void));
239 void CreateXPMPieces P((void));
240 void CreatePieces P((void));
241 void CreatePieceMenus P((void));
242 Widget CreateMenuBar P((Menu *mb));
243 Widget CreateButtonBar P ((MenuItem *mi));
244 char *FindFont P((char *pattern, int targetPxlSize));
245 void PieceMenuPopup P((Widget w, XEvent *event,
246                        String *params, Cardinal *num_params));
247 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
248 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
249 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
250                    u_int wreq, u_int hreq));
251 void CreateGrid P((void));
252 int EventToSquare P((int x, int limit));
253 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
254 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
255 void HandleUserMove P((Widget w, XEvent *event,
256                      String *prms, Cardinal *nprms));
257 void AnimateUserMove P((Widget w, XEvent * event,
258                      String * params, Cardinal * nParams));
259 void WhiteClock P((Widget w, XEvent *event,
260                    String *prms, Cardinal *nprms));
261 void BlackClock P((Widget w, XEvent *event,
262                    String *prms, Cardinal *nprms));
263 void DrawPositionProc P((Widget w, XEvent *event,
264                      String *prms, Cardinal *nprms));
265 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
266                      Board board));
267 void CommentPopUp P((char *title, char *label));
268 void CommentPopDown P((void));
269 void CommentCallback P((Widget w, XtPointer client_data,
270                         XtPointer call_data));
271 void ICSInputBoxPopUp P((void));
272 void ICSInputBoxPopDown P((void));
273 void FileNamePopUp P((char *label, char *def,
274                       FileProc proc, char *openMode));
275 void FileNamePopDown P((void));
276 void FileNameCallback P((Widget w, XtPointer client_data,
277                          XtPointer call_data));
278 void FileNameAction P((Widget w, XEvent *event,
279                        String *prms, Cardinal *nprms));
280 void AskQuestionReplyAction P((Widget w, XEvent *event,
281                           String *prms, Cardinal *nprms));
282 void AskQuestionProc P((Widget w, XEvent *event,
283                           String *prms, Cardinal *nprms));
284 void AskQuestionPopDown P((void));
285 void PromotionPopDown P((void));
286 void PromotionCallback P((Widget w, XtPointer client_data,
287                           XtPointer call_data));
288 void EditCommentPopDown P((void));
289 void EditCommentCallback P((Widget w, XtPointer client_data,
290                             XtPointer call_data));
291 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
292 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
293 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
294 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
295                          Cardinal *nprms));
296 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
297                          Cardinal *nprms));
298 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
299                        Cardinal *nprms));
300 void LoadPositionProc P((Widget w, XEvent *event,
301                          String *prms, Cardinal *nprms));
302 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
303                          Cardinal *nprms));
304 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
305                          Cardinal *nprms));
306 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
307                        Cardinal *nprms));
308 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
309                          Cardinal *nprms));
310 void PastePositionProc P((Widget w, XEvent *event, String *prms,
311                           Cardinal *nprms));
312 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
313 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
314 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
315 void SavePositionProc P((Widget w, XEvent *event,
316                          String *prms, Cardinal *nprms));
317 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
318 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
319                             Cardinal *nprms));
320 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
321 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
322 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
323                          Cardinal *nprms));
324 void MachineWhiteProc P((Widget w, XEvent *event,
325                          String *prms, Cardinal *nprms));
326 void AnalyzeModeProc P((Widget w, XEvent *event,
327                          String *prms, Cardinal *nprms));
328 void AnalyzeFileProc P((Widget w, XEvent *event,
329                          String *prms, Cardinal *nprms));
330 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
331                         Cardinal *nprms));
332 void IcsClientProc P((Widget w, XEvent *event, String *prms,
333                       Cardinal *nprms));
334 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
335 void EditPositionProc P((Widget w, XEvent *event,
336                          String *prms, Cardinal *nprms));
337 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void EditCommentProc P((Widget w, XEvent *event,
339                         String *prms, Cardinal *nprms));
340 void IcsInputBoxProc P((Widget w, XEvent *event,
341                         String *prms, Cardinal *nprms));
342 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
343 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
344 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
345 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
346 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
347 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
348 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
349 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
350 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
351 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
352 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
353 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
354 void StopObservingProc P((Widget w, XEvent *event, String *prms,
355                           Cardinal *nprms));
356 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
357                           Cardinal *nprms));
358 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
359 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
362 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
363 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
364                          Cardinal *nprms));
365 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
366                         Cardinal *nprms));
367 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
368 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
369                         Cardinal *nprms));
370 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
371                          Cardinal *nprms));
372 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
373                          Cardinal *nprms));
374 void AutocommProc P((Widget w, XEvent *event, String *prms,
375                      Cardinal *nprms));
376 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void AutobsProc P((Widget w, XEvent *event, String *prms,
379                         Cardinal *nprms));
380 void AutoraiseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void AutosaveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
384                        Cardinal *nprms));
385 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
386 void GetMoveListProc P((Widget w, XEvent *event, String *prms,
387                         Cardinal *nprms));
388 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
389                               Cardinal *nprms));
390 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
391                               Cardinal *nprms));
392 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
393 void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 void OldSaveStyleProc P((Widget w, XEvent *event, String *prms,
395                          Cardinal *nprms));
396 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
397                          Cardinal *nprms));
398 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
399                            Cardinal *nprms));
400 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
401                         Cardinal *nprms));
402 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
403                              Cardinal *nprms));
404 void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
405 void QuietPlayProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
406 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
407                        Cardinal *nprms));
408 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
409                          Cardinal *nprms));
410 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
411                          Cardinal *nprms));
412 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
413                           Cardinal *nprms));
414 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
415 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
416 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
417 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
418 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
419 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
420 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
421 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
422 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
423 void DisplayMove P((int moveNumber));
424 void DisplayTitle P((char *title));
425 void ICSInitScript P((void));
426 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
427 void ErrorPopUp P((char *title, char *text, int modal));
428 void ErrorPopDown P((void));
429 static char *ExpandPathName P((char *path));
430 static void CreateAnimVars P((void));
431 static void DragPieceMove P((int x, int y));
432 static void DrawDragPiece P((void));
433 char *ModeToWidgetName P((GameMode mode));
434 void EngineOutputUpdate( FrontEndProgramStats * stats );
435 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
436 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
437 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
438 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
439 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
440 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
441 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
442 void ShufflePopDown P(());
443 void EnginePopDown P(());
444 void UciPopDown P(());
445 void TimeControlPopDown P(());
446 void NewVariantPopDown P(());
447 void SettingsPopDown P(());
448 void update_ics_width P(());
449 int get_term_width P(());
450 /*
451 * XBoard depends on Xt R4 or higher
452 */
453 int xtVersion = XtSpecificationRelease;
454
455 int xScreen;
456 Display *xDisplay;
457 Window xBoardWindow;
458 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
459   jailSquareColor, highlightSquareColor, premoveHighlightColor;
460 Pixel lowTimeWarningColor;
461 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
462   bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
463   wjPieceGC, bjPieceGC, prelineGC, countGC;
464 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
465 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
466   whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
467   commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
468   menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
469   ICSInputShell, fileNameShell, askQuestionShell;
470 XSegment gridSegments[(BOARD_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         if (fromX >= 0 && fromY >= 0) {
4553             drawHighlight(fromX, fromY, highlineGC);
4554         }
4555     }
4556     if (hi2X != toX || hi2Y != toY) {
4557         if (hi2X >= 0 && hi2Y >= 0) {
4558             drawHighlight(hi2X, hi2Y, lineGC);
4559         }
4560         if (toX >= 0 && toY >= 0) {
4561             drawHighlight(toX, toY, highlineGC);
4562         }
4563     }
4564     hi1X = fromX;
4565     hi1Y = fromY;
4566     hi2X = toX;
4567     hi2Y = toY;
4568 }
4569
4570 void
4571 ClearHighlights()
4572 {
4573     SetHighlights(-1, -1, -1, -1);
4574 }
4575
4576
4577 void
4578 SetPremoveHighlights(fromX, fromY, toX, toY)
4579      int fromX, fromY, toX, toY;
4580 {
4581     if (pm1X != fromX || pm1Y != fromY) {
4582         if (pm1X >= 0 && pm1Y >= 0) {
4583             drawHighlight(pm1X, pm1Y, lineGC);
4584         }
4585         if (fromX >= 0 && fromY >= 0) {
4586             drawHighlight(fromX, fromY, prelineGC);
4587         }
4588     }
4589     if (pm2X != toX || pm2Y != toY) {
4590         if (pm2X >= 0 && pm2Y >= 0) {
4591             drawHighlight(pm2X, pm2Y, lineGC);
4592         }
4593         if (toX >= 0 && toY >= 0) {
4594             drawHighlight(toX, toY, prelineGC);
4595         }
4596     }
4597     pm1X = fromX;
4598     pm1Y = fromY;
4599     pm2X = toX;
4600     pm2Y = toY;
4601 }
4602
4603 void
4604 ClearPremoveHighlights()
4605 {
4606   SetPremoveHighlights(-1, -1, -1, -1);
4607 }
4608
4609 static void BlankSquare(x, y, color, piece, dest)
4610      int x, y, color;
4611      ChessSquare piece;
4612      Drawable dest;
4613 {
4614     if (useImages && useImageSqs) {
4615         Pixmap pm;
4616         switch (color) {
4617           case 1: /* light */
4618             pm = xpmLightSquare;
4619             break;
4620           case 0: /* dark */
4621             pm = xpmDarkSquare;
4622             break;
4623           case 2: /* neutral */
4624           default:
4625             pm = xpmJailSquare;
4626             break;
4627         }
4628         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4629                   squareSize, squareSize, x, y);
4630     } else {
4631         GC gc;
4632         switch (color) {
4633           case 1: /* light */
4634             gc = lightSquareGC;
4635             break;
4636           case 0: /* dark */
4637             gc = darkSquareGC;
4638             break;
4639           case 2: /* neutral */
4640           default:
4641             gc = jailSquareGC;
4642             break;
4643         }
4644         XFillRectangle(xDisplay, dest, gc, x, y, squareSize, squareSize);
4645     }
4646 }
4647
4648 /*
4649    I split out the routines to draw a piece so that I could
4650    make a generic flash routine.
4651 */
4652 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4653      ChessSquare piece;
4654      int square_color, x, y;
4655      Drawable dest;
4656 {
4657     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4658     switch (square_color) {
4659       case 1: /* light */
4660       case 2: /* neutral */
4661       default:
4662         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4663                   ? *pieceToOutline(piece)
4664                   : *pieceToSolid(piece),
4665                   dest, bwPieceGC, 0, 0,
4666                   squareSize, squareSize, x, y);
4667         break;
4668       case 0: /* dark */
4669         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4670                   ? *pieceToSolid(piece)
4671                   : *pieceToOutline(piece),
4672                   dest, wbPieceGC, 0, 0,
4673                   squareSize, squareSize, x, y);
4674         break;
4675     }
4676 }
4677
4678 static void monoDrawPiece(piece, square_color, x, y, dest)
4679      ChessSquare piece;
4680      int square_color, x, y;
4681      Drawable dest;
4682 {
4683     switch (square_color) {
4684       case 1: /* light */
4685       case 2: /* neutral */
4686       default:
4687         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4688                    ? *pieceToOutline(piece)
4689                    : *pieceToSolid(piece),
4690                    dest, bwPieceGC, 0, 0,
4691                    squareSize, squareSize, x, y, 1);
4692         break;
4693       case 0: /* dark */
4694         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4695                    ? *pieceToSolid(piece)
4696                    : *pieceToOutline(piece),
4697                    dest, wbPieceGC, 0, 0,
4698                    squareSize, squareSize, x, y, 1);
4699         break;
4700     }
4701 }
4702
4703 static void colorDrawPiece(piece, square_color, x, y, dest)
4704      ChessSquare piece;
4705      int square_color, x, y;
4706      Drawable dest;
4707 {
4708     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4709     switch (square_color) {
4710       case 1: /* light */
4711         XCopyPlane(xDisplay, *pieceToSolid(piece),
4712                    dest, (int) piece < (int) BlackPawn
4713                    ? wlPieceGC : blPieceGC, 0, 0,
4714                    squareSize, squareSize, x, y, 1);
4715         break;
4716       case 0: /* dark */
4717         XCopyPlane(xDisplay, *pieceToSolid(piece),
4718                    dest, (int) piece < (int) BlackPawn
4719                    ? wdPieceGC : bdPieceGC, 0, 0,
4720                    squareSize, squareSize, x, y, 1);
4721         break;
4722       case 2: /* neutral */
4723       default:
4724         XCopyPlane(xDisplay, *pieceToSolid(piece),
4725                    dest, (int) piece < (int) BlackPawn
4726                    ? wjPieceGC : bjPieceGC, 0, 0,
4727                    squareSize, squareSize, x, y, 1);
4728         break;
4729     }
4730 }
4731
4732 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4733      ChessSquare piece;
4734      int square_color, x, y;
4735      Drawable dest;
4736 {
4737     int kind;
4738
4739     switch (square_color) {
4740       case 1: /* light */
4741       case 2: /* neutral */
4742       default:
4743         if ((int)piece < (int) BlackPawn) {
4744             kind = 0;
4745         } else {
4746             kind = 2;
4747             piece -= BlackPawn;
4748         }
4749         break;
4750       case 0: /* dark */
4751         if ((int)piece < (int) BlackPawn) {
4752             kind = 1;
4753         } else {
4754             kind = 3;
4755             piece -= BlackPawn;
4756         }
4757         break;
4758     }
4759     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4760               dest, wlPieceGC, 0, 0,
4761               squareSize, squareSize, x, y);
4762 }
4763
4764 typedef void (*DrawFunc)();
4765
4766 DrawFunc ChooseDrawFunc()
4767 {
4768     if (appData.monoMode) {
4769         if (DefaultDepth(xDisplay, xScreen) == 1) {
4770             return monoDrawPiece_1bit;
4771         } else {
4772             return monoDrawPiece;
4773         }
4774     } else {
4775         if (useImages)
4776           return colorDrawPieceImage;
4777         else
4778           return colorDrawPiece;
4779     }
4780 }
4781
4782 /* [HR] determine square color depending on chess variant. */
4783 static int SquareColor(row, column)
4784      int row, column;
4785 {
4786     int square_color;
4787
4788     if (gameInfo.variant == VariantXiangqi) {
4789         if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4790             square_color = 1;
4791         } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4792             square_color = 0;
4793         } else if (row <= 4) {
4794             square_color = 0;
4795         } else {
4796             square_color = 1;
4797         }
4798     } else {
4799         square_color = ((column + row) % 2) == 1;
4800     }
4801
4802     /* [hgm] holdings: next line makes all holdings squares light */
4803     if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4804
4805     return square_color;
4806 }
4807
4808 void DrawSquare(row, column, piece, do_flash)
4809      int row, column, do_flash;
4810      ChessSquare piece;
4811 {
4812     int square_color, x, y, direction, font_ascent, font_descent;
4813     int i;
4814     char string[2];
4815     XCharStruct overall;
4816     DrawFunc drawfunc;
4817     int flash_delay;
4818
4819     /* Calculate delay in milliseconds (2-delays per complete flash) */
4820     flash_delay = 500 / appData.flashRate;
4821
4822     if (flipView) {
4823         x = lineGap + ((BOARD_WIDTH-1)-column) *
4824           (squareSize + lineGap);
4825         y = lineGap + row * (squareSize + lineGap);
4826     } else {
4827         x = lineGap + column * (squareSize + lineGap);
4828         y = lineGap + ((BOARD_HEIGHT-1)-row) *
4829           (squareSize + lineGap);
4830     }
4831
4832     square_color = SquareColor(row, column);
4833
4834     if ( // [HGM] holdings: blank out area between board and holdings
4835                  column == BOARD_LEFT-1 ||  column == BOARD_RGHT
4836               || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4837                   || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4838                         BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
4839
4840                         // [HGM] print piece counts next to holdings
4841                         string[1] = NULLCHAR;
4842                         if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4843                             string[0] = '0' + piece;
4844                             XTextExtents(countFontStruct, string, 1, &direction,
4845                                  &font_ascent, &font_descent, &overall);
4846                             if (appData.monoMode) {
4847                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4848                                                  x + squareSize - overall.width - 2,
4849                                                  y + font_ascent + 1, string, 1);
4850                             } else {
4851                                 XDrawString(xDisplay, xBoardWindow, countGC,
4852                                             x + squareSize - overall.width - 2,
4853                                             y + font_ascent + 1, string, 1);
4854                             }
4855                         }
4856                         if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4857                             string[0] = '0' + piece;
4858                             XTextExtents(countFontStruct, string, 1, &direction,
4859                                          &font_ascent, &font_descent, &overall);
4860                             if (appData.monoMode) {
4861                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4862                                                  x + 2, y + font_ascent + 1, string, 1);
4863                             } else {
4864                                 XDrawString(xDisplay, xBoardWindow, countGC,
4865                                             x + 2, y + font_ascent + 1, string, 1);
4866                             }
4867                         }
4868     } else {
4869             if (piece == EmptySquare || appData.blindfold) {
4870                         BlankSquare(x, y, square_color, piece, xBoardWindow);
4871             } else {
4872                         drawfunc = ChooseDrawFunc();
4873                         if (do_flash && appData.flashCount > 0) {
4874                             for (i=0; i<appData.flashCount; ++i) {
4875
4876                                         drawfunc(piece, square_color, x, y, xBoardWindow);
4877                                         XSync(xDisplay, False);
4878                                         do_flash_delay(flash_delay);
4879
4880                                         BlankSquare(x, y, square_color, piece, xBoardWindow);
4881                                         XSync(xDisplay, False);
4882                                         do_flash_delay(flash_delay);
4883                             }
4884                         }
4885                         drawfunc(piece, square_color, x, y, xBoardWindow);
4886         }
4887         }
4888
4889     string[1] = NULLCHAR;
4890     if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4891                 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4892         string[0] = 'a' + column - BOARD_LEFT;
4893         XTextExtents(coordFontStruct, string, 1, &direction,
4894                      &font_ascent, &font_descent, &overall);
4895         if (appData.monoMode) {
4896             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4897                              x + squareSize - overall.width - 2,
4898                              y + squareSize - font_descent - 1, string, 1);
4899         } else {
4900             XDrawString(xDisplay, xBoardWindow, coordGC,
4901                         x + squareSize - overall.width - 2,
4902                         y + squareSize - font_descent - 1, string, 1);
4903         }
4904     }
4905     if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4906         string[0] = ONE + row;
4907         XTextExtents(coordFontStruct, string, 1, &direction,
4908                      &font_ascent, &font_descent, &overall);
4909         if (appData.monoMode) {
4910             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4911                              x + 2, y + font_ascent + 1, string, 1);
4912         } else {
4913             XDrawString(xDisplay, xBoardWindow, coordGC,
4914                         x + 2, y + font_ascent + 1, string, 1);
4915         }
4916     }
4917 }
4918
4919
4920 /* Why is this needed on some versions of X? */
4921 void EventProc(widget, unused, event)
4922      Widget widget;
4923      caddr_t unused;
4924      XEvent *event;
4925 {
4926     if (!XtIsRealized(widget))
4927       return;
4928
4929     switch (event->type) {
4930       case Expose:
4931         if (event->xexpose.count > 0) return;  /* no clipping is done */
4932         XDrawPosition(widget, True, NULL);
4933         break;
4934       default:
4935         return;
4936     }
4937 }
4938 /* end why */
4939
4940 void DrawPosition(fullRedraw, board)
4941      /*Boolean*/int fullRedraw;
4942      Board board;
4943 {
4944     XDrawPosition(boardWidget, fullRedraw, board);
4945 }
4946
4947 /* Returns 1 if there are "too many" differences between b1 and b2
4948    (i.e. more than 1 move was made) */
4949 static int too_many_diffs(b1, b2)
4950      Board b1, b2;
4951 {
4952     int i, j;
4953     int c = 0;
4954
4955     for (i=0; i<BOARD_HEIGHT; ++i) {
4956         for (j=0; j<BOARD_WIDTH; ++j) {
4957             if (b1[i][j] != b2[i][j]) {
4958                 if (++c > 4)    /* Castling causes 4 diffs */
4959                   return 1;
4960             }
4961         }
4962     }
4963
4964     return 0;
4965 }
4966
4967 /* Matrix describing castling maneuvers */
4968 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4969 static int castling_matrix[4][5] = {
4970     { 0, 0, 4, 3, 2 },          /* 0-0-0, white */
4971     { 0, 7, 4, 5, 6 },          /* 0-0,   white */
4972     { 7, 0, 4, 3, 2 },          /* 0-0-0, black */
4973     { 7, 7, 4, 5, 6 }           /* 0-0,   black */
4974 };
4975
4976 /* Checks whether castling occurred. If it did, *rrow and *rcol
4977    are set to the destination (row,col) of the rook that moved.
4978
4979    Returns 1 if castling occurred, 0 if not.
4980
4981    Note: Only handles a max of 1 castling move, so be sure
4982    to call too_many_diffs() first.
4983    */
4984 static int check_castle_draw(newb, oldb, rrow, rcol)
4985      Board newb, oldb;
4986      int *rrow, *rcol;
4987 {
4988     int i, *r, j;
4989     int match;
4990
4991     /* For each type of castling... */
4992     for (i=0; i<4; ++i) {
4993         r = castling_matrix[i];
4994
4995         /* Check the 4 squares involved in the castling move */
4996         match = 0;
4997         for (j=1; j<=4; ++j) {
4998             if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4999                 match = 1;
5000                 break;
5001             }
5002         }
5003
5004         if (!match) {
5005             /* All 4 changed, so it must be a castling move */
5006             *rrow = r[0];
5007             *rcol = r[3];
5008             return 1;
5009         }
5010     }
5011     return 0;
5012 }
5013
5014 static int damage[BOARD_SIZE][BOARD_SIZE];
5015
5016 /*
5017  * event handler for redrawing the board
5018  */
5019 void XDrawPosition(w, repaint, board)
5020      Widget w;
5021      /*Boolean*/int repaint;
5022      Board board;
5023 {
5024     int i, j, do_flash;
5025     static int lastFlipView = 0;
5026     static int lastBoardValid = 0;
5027     static Board lastBoard;
5028     Arg args[16];
5029     int rrow, rcol;
5030
5031     if (board == NULL) {
5032         if (!lastBoardValid) return;
5033         board = lastBoard;
5034     }
5035     if (!lastBoardValid || lastFlipView != flipView) {
5036         XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
5037         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
5038                     args, 1);
5039     }
5040
5041     /*
5042      * It would be simpler to clear the window with XClearWindow()
5043      * but this causes a very distracting flicker.
5044      */
5045
5046     if (!repaint && lastBoardValid && lastFlipView == flipView) {
5047
5048         /* If too much changes (begin observing new game, etc.), don't
5049            do flashing */
5050         do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
5051
5052         /* Special check for castling so we don't flash both the king
5053            and the rook (just flash the king). */
5054         if (do_flash) {
5055             if (check_castle_draw(board, lastBoard, &rrow, &rcol)) {
5056                 /* Draw rook with NO flashing. King will be drawn flashing later */
5057                 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
5058                 lastBoard[rrow][rcol] = board[rrow][rcol];
5059             }
5060         }
5061
5062         /* First pass -- Draw (newly) empty squares and repair damage.
5063            This prevents you from having a piece show up twice while it
5064            is flashing on its new square */
5065         for (i = 0; i < BOARD_HEIGHT; i++)
5066           for (j = 0; j < BOARD_WIDTH; j++)
5067             if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
5068                 || damage[i][j]) {
5069                 DrawSquare(i, j, board[i][j], 0);
5070                 damage[i][j] = False;
5071             }
5072
5073         /* Second pass -- Draw piece(s) in new position and flash them */
5074         for (i = 0; i < BOARD_HEIGHT; i++)
5075           for (j = 0; j < BOARD_WIDTH; j++)
5076             if (board[i][j] != lastBoard[i][j]) {
5077                 DrawSquare(i, j, board[i][j], do_flash);
5078             }
5079     } else {
5080         if (lineGap > 0)
5081           XDrawSegments(xDisplay, xBoardWindow, lineGC,
5082                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
5083
5084         for (i = 0; i < BOARD_HEIGHT; i++)
5085           for (j = 0; j < BOARD_WIDTH; j++) {
5086               DrawSquare(i, j, board[i][j], 0);
5087               damage[i][j] = False;
5088           }
5089     }
5090
5091     CopyBoard(lastBoard, board);
5092     lastBoardValid = 1;
5093     lastFlipView = flipView;
5094
5095     /* Draw highlights */
5096     if (pm1X >= 0 && pm1Y >= 0) {
5097       drawHighlight(pm1X, pm1Y, prelineGC);
5098     }
5099     if (pm2X >= 0 && pm2Y >= 0) {
5100       drawHighlight(pm2X, pm2Y, prelineGC);
5101     }
5102     if (hi1X >= 0 && hi1Y >= 0) {
5103       drawHighlight(hi1X, hi1Y, highlineGC);
5104     }
5105     if (hi2X >= 0 && hi2Y >= 0) {
5106       drawHighlight(hi2X, hi2Y, highlineGC);
5107     }
5108
5109     /* If piece being dragged around board, must redraw that too */
5110     DrawDragPiece();
5111
5112     XSync(xDisplay, False);
5113 }
5114
5115
5116 /*
5117  * event handler for redrawing the board
5118  */
5119 void DrawPositionProc(w, event, prms, nprms)
5120      Widget w;
5121      XEvent *event;
5122      String *prms;
5123      Cardinal *nprms;
5124 {
5125     XDrawPosition(w, True, NULL);
5126 }
5127
5128
5129 /*
5130  * event handler for parsing user moves
5131  */
5132 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
5133 //       way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
5134 //       it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
5135 //       should be made to use the new way, of calling UserMoveTest early  to determine the legality of the
5136 //       move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
5137 //       and at the end FinishMove() to perform the move after optional promotion popups.
5138 //       For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
5139 void HandleUserMove(w, event, prms, nprms)
5140      Widget w;
5141      XEvent *event;
5142      String *prms;
5143      Cardinal *nprms;
5144 {
5145     if (w != boardWidget || errorExitStatus != -1) return;
5146
5147     if (promotionUp) {
5148         if (event->type == ButtonPress) {
5149             XtPopdown(promotionShell);
5150             XtDestroyWidget(promotionShell);
5151             promotionUp = False;
5152             ClearHighlights();
5153             fromX = fromY = -1;
5154         } else {
5155             return;
5156         }
5157     }
5158
5159     // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
5160     if(event->type == ButtonPress)   LeftClick(Press,   event->xbutton.x, event->xbutton.y);
5161     if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
5162 }
5163
5164 void AnimateUserMove (Widget w, XEvent * event,
5165                       String * params, Cardinal * nParams)
5166 {
5167     DragPieceMove(event->xmotion.x, event->xmotion.y);
5168 }
5169
5170 Widget CommentCreate(name, text, mutable, callback, lines)
5171      char *name, *text;
5172      int /*Boolean*/ mutable;
5173      XtCallbackProc callback;
5174      int lines;
5175 {
5176     Arg args[16];
5177     Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
5178     Dimension bw_width;
5179     int j;
5180
5181     j = 0;
5182     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
5183     XtGetValues(boardWidget, args, j);
5184
5185     j = 0;
5186     XtSetArg(args[j], XtNresizable, True);  j++;
5187 #if TOPLEVEL
5188     shell =
5189       XtCreatePopupShell(name, topLevelShellWidgetClass,
5190                          shellWidget, args, j);
5191 #else
5192     shell =
5193       XtCreatePopupShell(name, transientShellWidgetClass,
5194                          shellWidget, args, j);
5195 #endif
5196     layout =
5197       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
5198                             layoutArgs, XtNumber(layoutArgs));
5199     form =
5200       XtCreateManagedWidget("form", formWidgetClass, layout,
5201                             formArgs, XtNumber(formArgs));
5202
5203     j = 0;
5204     if (mutable) {
5205         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
5206         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
5207     }
5208     XtSetArg(args[j], XtNstring, text);  j++;
5209     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
5210     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
5211     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
5212     XtSetArg(args[j], XtNright, XtChainRight);  j++;
5213     XtSetArg(args[j], XtNresizable, True);  j++;
5214     XtSetArg(args[j], XtNwidth, bw_width);  j++; /*force wider than buttons*/
5215     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
5216     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
5217     XtSetArg(args[j], XtNautoFill, True);  j++;
5218     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
5219     edit =
5220       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
5221
5222     if (mutable) {
5223         j = 0;
5224         XtSetArg(args[j], XtNfromVert, edit);  j++;
5225         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
5226         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
5227         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
5228         XtSetArg(args[j], XtNright, XtChainLeft); j++;
5229         b_ok =
5230           XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
5231         XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
5232
5233         j = 0;
5234         XtSetArg(args[j], XtNfromVert, edit);  j++;
5235         XtSetArg(args[j], XtNfromHoriz, b_ok);  j++;
5236         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
5237         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
5238         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
5239         XtSetArg(args[j], XtNright, XtChainLeft); j++;
5240         b_cancel =
5241           XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
5242         XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
5243
5244         j = 0;
5245         XtSetArg(args[j], XtNfromVert, edit);  j++;
5246         XtSetArg(args[j], XtNfromHoriz, b_cancel);  j++;
5247         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
5248         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
5249         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
5250         XtSetArg(args[j], XtNright, XtChainLeft); j++;
5251         b_clear =
5252           XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
5253         XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
5254     } else {
5255         j = 0;
5256         XtSetArg(args[j], XtNfromVert, edit);  j++;
5257         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
5258         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
5259         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
5260         XtSetArg(args[j], XtNright, XtChainLeft); j++;
5261         b_close =
5262           XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
5263         XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
5264
5265         j = 0;
5266         XtSetArg(args[j], XtNfromVert, edit);  j++;
5267         XtSetArg(args[j], XtNfromHoriz, b_close);  j++;
5268         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
5269         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
5270         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
5271         XtSetArg(args[j], XtNright, XtChainLeft); j++;
5272         b_edit =
5273           XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
5274         XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
5275     }
5276
5277     XtRealizeWidget(shell);
5278
5279     if (commentX == -1) {
5280         int xx, yy;
5281         Window junk;
5282         Dimension pw_height;
5283         Dimension ew_height;
5284
5285         j = 0;
5286         XtSetArg(args[j], XtNheight, &ew_height);  j++;
5287         XtGetValues(edit, args, j);
5288
5289         j = 0;
5290         XtSetArg(args[j], XtNheight, &pw_height);  j++;
5291         XtGetValues(shell, args, j);
5292         commentH = pw_height + (lines - 1) * ew_height;
5293         commentW = bw_width - 16;
5294
5295         XSync(xDisplay, False);
5296 #ifdef NOTDEF
5297         /* This code seems to tickle an X bug if it is executed too soon
5298            after xboard starts up.  The coordinates get transformed as if
5299            the main window was positioned at (0, 0).
5300            */
5301         XtTranslateCoords(shellWidget,
5302                           (bw_width - commentW) / 2, 0 - commentH / 2,
5303                           &commentX, &commentY);
5304 #else  /*!NOTDEF*/
5305         XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
5306                               RootWindowOfScreen(XtScreen(shellWidget)),
5307                               (bw_width - commentW) / 2, 0 - commentH / 2,
5308                               &xx, &yy, &junk);
5309         commentX = xx;
5310         commentY = yy;
5311 #endif /*!NOTDEF*/
5312         if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
5313     }
5314     j = 0;
5315     XtSetArg(args[j], XtNheight, commentH);  j++;
5316     XtSetArg(args[j], XtNwidth, commentW);  j++;
5317     XtSetArg(args[j], XtNx, commentX);  j++;
5318     XtSetArg(args[j], XtNy, commentY);  j++;
5319     XtSetValues(shell, args, j);
5320     XtSetKeyboardFocus(shell, edit);
5321
5322     return shell;
5323 }
5324
5325 /* Used for analysis window and ICS input window */
5326 Widget MiscCreate(name, text, mutable, callback, lines)
5327      char *name, *text;
5328      int /*Boolean*/ mutable;
5329      XtCallbackProc callback;
5330      int lines;
5331 {
5332     Arg args[16];
5333     Widget shell, layout, form, edit;
5334     Position x, y;
5335     Dimension bw_width, pw_height, ew_height, w, h;
5336     int j;
5337     int xx, yy;
5338     Window junk;
5339
5340     j = 0;
5341     XtSetArg(args[j], XtNresizable, True);  j++;
5342 #if TOPLEVEL
5343     shell =
5344       XtCreatePopupShell(name, topLevelShellWidgetClass,
5345                          shellWidget, args, j);
5346 #else
5347     shell =
5348       XtCreatePopupShell(name, transientShellWidgetClass,
5349                          shellWidget, args, j);
5350 #endif
5351     layout =
5352       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
5353                             layoutArgs, XtNumber(layoutArgs));
5354     form =
5355       XtCreateManagedWidget("form", formWidgetClass, layout,
5356                             formArgs, XtNumber(formArgs));
5357
5358     j = 0;
5359     if (mutable) {
5360         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
5361         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
5362     }
5363     XtSetArg(args[j], XtNstring, text);  j++;
5364     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
5365     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
5366     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
5367     XtSetArg(args[j], XtNright, XtChainRight);  j++;
5368     XtSetArg(args[j], XtNresizable, True);  j++;
5369     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
5370     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
5371     XtSetArg(args[j], XtNautoFill, True);  j++;
5372     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
5373     edit =
5374       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
5375
5376     XtRealizeWidget(shell);
5377
5378     j = 0;
5379     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
5380     XtGetValues(boardWidget, args, j);
5381
5382     j = 0;
5383     XtSetArg(args[j], XtNheight, &ew_height);  j++;
5384     XtGetValues(edit, args, j);
5385
5386     j = 0;
5387     XtSetArg(args[j], XtNheight, &pw_height);  j++;
5388     XtGetValues(shell, args, j);
5389     h = pw_height + (lines - 1) * ew_height;
5390     w = bw_width - 16;
5391
5392     XSync(xDisplay, False);
5393 #ifdef NOTDEF
5394     /* This code seems to tickle an X bug if it is executed too soon
5395        after xboard starts up.  The coordinates get transformed as if
5396        the main window was positioned at (0, 0).
5397     */
5398     XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
5399 #else  /*!NOTDEF*/
5400     XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
5401                           RootWindowOfScreen(XtScreen(shellWidget)),
5402                           (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
5403 #endif /*!NOTDEF*/
5404     x = xx;
5405     y = yy;
5406     if (y < 0) y = 0; /*avoid positioning top offscreen*/
5407
5408     j = 0;
5409     XtSetArg(args[j], XtNheight, h);  j++;
5410     XtSetArg(args[j], XtNwidth, w);  j++;
5411     XtSetArg(args[j], XtNx, x);  j++;
5412     XtSetArg(args[j], XtNy, y);  j++;
5413     XtSetValues(shell, args, j);
5414
5415     return shell;
5416 }
5417
5418
5419 static int savedIndex;  /* gross that this is global */
5420
5421 void EditCommentPopUp(index, title, text)
5422      int index;
5423      char *title, *text;
5424 {
5425     Widget edit;
5426     Arg args[16];
5427     int j;
5428
5429     savedIndex = index;
5430     if (text == NULL) text = "";
5431
5432     if (editShell == NULL) {
5433         editShell =
5434           CommentCreate(title, text, True, EditCommentCallback, 4);
5435         XtRealizeWidget(editShell);
5436         CatchDeleteWindow(editShell, "EditCommentPopDown");
5437     } else {
5438         edit = XtNameToWidget(editShell, "*form.text");
5439         j = 0;
5440         XtSetArg(args[j], XtNstring, text); j++;
5441         XtSetValues(edit, args, j);
5442         j = 0;
5443         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
5444         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
5445         XtSetValues(editShell, args, j);
5446     }
5447
5448     XtPopup(editShell, XtGrabNone);
5449
5450     editUp = True;
5451     j = 0;
5452     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
5453     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
5454                 args, j);
5455 }
5456
5457 void EditCommentCallback(w, client_data, call_data)
5458      Widget w;
5459      XtPointer client_data, call_data;
5460 {
5461     String name, val;
5462     Arg args[16];
5463     int j;
5464     Widget edit;
5465
5466     j = 0;
5467     XtSetArg(args[j], XtNlabel, &name);  j++;
5468     XtGetValues(w, args, j);
5469
5470     if (strcmp(name, _("ok")) == 0) {
5471         edit = XtNameToWidget(editShell, "*form.text");
5472         j = 0;
5473         XtSetArg(args[j], XtNstring, &val); j++;
5474         XtGetValues(edit, args, j);
5475         ReplaceComment(savedIndex, val);
5476         EditCommentPopDown();
5477     } else if (strcmp(name, _("cancel")) == 0) {
5478         EditCommentPopDown();
5479     } else if (strcmp(name, _("clear")) == 0) {
5480         edit = XtNameToWidget(editShell, "*form.text");
5481         XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5482         XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5483     }
5484 }
5485
5486 void EditCommentPopDown()
5487 {
5488     Arg args[16];
5489     int j;
5490
5491     if (!editUp) return;
5492     j = 0;
5493     XtSetArg(args[j], XtNx, &commentX); j++;
5494     XtSetArg(args[j], XtNy, &commentY); j++;
5495     XtSetArg(args[j], XtNheight, &commentH); j++;
5496     XtSetArg(args[j], XtNwidth, &commentW); j++;
5497     XtGetValues(editShell, args, j);
5498     XtPopdown(editShell);
5499     editUp = False;
5500     j = 0;
5501     XtSetArg(args[j], XtNleftBitmap, None); j++;
5502     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
5503                 args, j);
5504 }
5505
5506 void ICSInputBoxPopUp()
5507 {
5508     Widget edit;
5509     Arg args[16];
5510     int j;
5511     char *title = _("ICS Input");
5512     XtTranslations tr;
5513
5514     if (ICSInputShell == NULL) {
5515         ICSInputShell = MiscCreate(title, "", True, NULL, 1);
5516         tr = XtParseTranslationTable(ICSInputTranslations);
5517         edit = XtNameToWidget(ICSInputShell, "*form.text");
5518         XtOverrideTranslations(edit, tr);
5519         XtRealizeWidget(ICSInputShell);
5520         CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
5521
5522     } else {
5523         edit = XtNameToWidget(ICSInputShell, "*form.text");
5524         j = 0;
5525         XtSetArg(args[j], XtNstring, ""); j++;
5526         XtSetValues(edit, args, j);
5527         j = 0;
5528         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
5529         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
5530         XtSetValues(ICSInputShell, args, j);
5531     }
5532
5533     XtPopup(ICSInputShell, XtGrabNone);
5534     XtSetKeyboardFocus(ICSInputShell, edit);
5535
5536     ICSInputBoxUp = True;
5537     j = 0;
5538     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
5539     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
5540                 args, j);
5541 }
5542
5543 void ICSInputSendText()
5544 {
5545     Widget edit;
5546     int j;
5547     Arg args[16];
5548     String val;
5549
5550     edit = XtNameToWidget(ICSInputShell, "*form.text");
5551     j = 0;
5552     XtSetArg(args[j], XtNstring, &val); j++;
5553     XtGetValues(edit, args, j);
5554     SendMultiLineToICS(val);
5555     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5556     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5557 }
5558
5559 void ICSInputBoxPopDown()
5560 {
5561     Arg args[16];
5562     int j;
5563
5564     if (!ICSInputBoxUp) return;
5565     j = 0;
5566     XtPopdown(ICSInputShell);
5567     ICSInputBoxUp = False;
5568     j = 0;
5569     XtSetArg(args[j], XtNleftBitmap, None); j++;
5570     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
5571                 args, j);
5572 }
5573
5574 void CommentPopUp(title, text)
5575      char *title, *text;
5576 {
5577     Arg args[16];
5578     int j;
5579     Widget edit;
5580
5581     if (commentShell == NULL) {
5582         commentShell =
5583           CommentCreate(title, text, False, CommentCallback, 4);
5584         XtRealizeWidget(commentShell);
5585         CatchDeleteWindow(commentShell, "CommentPopDown");
5586     } else {
5587         edit = XtNameToWidget(commentShell, "*form.text");
5588         j = 0;
5589         XtSetArg(args[j], XtNstring, text); j++;
5590         XtSetValues(edit, args, j);
5591         j = 0;
5592         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
5593         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
5594         XtSetValues(commentShell, args, j);
5595     }
5596
5597     XtPopup(commentShell, XtGrabNone);
5598     XSync(xDisplay, False);
5599
5600     commentUp = True;
5601 }
5602
5603 void CommentCallback(w, client_data, call_data)
5604      Widget w;
5605      XtPointer client_data, call_data;
5606 {
5607     String name;
5608     Arg args[16];
5609     int j;
5610
5611     j = 0;
5612     XtSetArg(args[j], XtNlabel, &name);  j++;
5613     XtGetValues(w, args, j);
5614
5615     if (strcmp(name, _("close")) == 0) {
5616         CommentPopDown();
5617     } else if (strcmp(name, _("edit")) == 0) {
5618         CommentPopDown();
5619         EditCommentEvent();
5620     }
5621 }
5622
5623
5624 void CommentPopDown()
5625 {
5626     Arg args[16];
5627     int j;
5628
5629     if (!commentUp) return;
5630     j = 0;
5631     XtSetArg(args[j], XtNx, &commentX); j++;
5632     XtSetArg(args[j], XtNy, &commentY); j++;
5633     XtSetArg(args[j], XtNwidth, &commentW); j++;
5634     XtSetArg(args[j], XtNheight, &commentH); j++;
5635     XtGetValues(commentShell, args, j);
5636     XtPopdown(commentShell);
5637     XSync(xDisplay, False);
5638     commentUp = False;
5639 }
5640
5641 void FileNamePopUp(label, def, proc, openMode)
5642      char *label;
5643      char *def;
5644      FileProc proc;
5645      char *openMode;
5646 {
5647     Arg args[16];
5648     Widget popup, layout, dialog, edit;
5649     Window root, child;
5650     int x, y, i;
5651     int win_x, win_y;
5652     unsigned int mask;
5653
5654     fileProc = proc;            /* I can't see a way not */
5655     fileOpenMode = openMode;    /*   to use globals here */
5656
5657     i = 0;
5658     XtSetArg(args[i], XtNresizable, True); i++;
5659     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
5660     XtSetArg(args[i], XtNtitle, XtNewString(_("File name prompt"))); i++;
5661     fileNameShell = popup =
5662       XtCreatePopupShell("File name prompt", transientShellWidgetClass,
5663                          shellWidget, args, i);
5664
5665     layout =
5666       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
5667                             layoutArgs, XtNumber(layoutArgs));
5668
5669     i = 0;
5670     XtSetArg(args[i], XtNlabel, label); i++;
5671     XtSetArg(args[i], XtNvalue, def); i++;
5672     XtSetArg(args[i], XtNborderWidth, 0); i++;
5673     dialog = XtCreateManagedWidget("fileName", dialogWidgetClass,
5674                                    layout, args, i);
5675
5676     XawDialogAddButton(dialog, _("ok"), FileNameCallback, (XtPointer) dialog);
5677     XawDialogAddButton(dialog, _("cancel"), FileNameCallback,
5678                        (XtPointer) dialog);
5679
5680     XtRealizeWidget(popup);
5681     CatchDeleteWindow(popup, "FileNamePopDown");
5682
5683     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
5684                   &x, &y, &win_x, &win_y, &mask);
5685
5686     XtSetArg(args[0], XtNx, x - 10);
5687     XtSetArg(args[1], XtNy, y - 30);
5688     XtSetValues(popup, args, 2);
5689
5690     XtPopup(popup, XtGrabExclusive);
5691     filenameUp = True;
5692
5693     edit = XtNameToWidget(dialog, "*value");
5694     XtSetKeyboardFocus(popup, edit);
5695 }
5696
5697 void FileNamePopDown()
5698 {
5699     if (!filenameUp) return;
5700     XtPopdown(fileNameShell);
5701     XtDestroyWidget(fileNameShell);
5702     filenameUp = False;
5703     ModeHighlight();
5704 }
5705
5706 void FileNameCallback(w, client_data, call_data)
5707      Widget w;
5708      XtPointer client_data, call_data;
5709 {
5710     String name;
5711     Arg args[16];
5712
5713     XtSetArg(args[0], XtNlabel, &name);
5714     XtGetValues(w, args, 1);
5715
5716     if (strcmp(name, _("cancel")) == 0) {
5717         FileNamePopDown();
5718         return;
5719     }
5720
5721     FileNameAction(w, NULL, NULL, NULL);
5722 }
5723
5724 void FileNameAction(w, event, prms, nprms)
5725      Widget w;
5726      XEvent *event;
5727      String *prms;
5728      Cardinal *nprms;
5729 {
5730     char buf[MSG_SIZ];
5731     String name;
5732     FILE *f;
5733     char *p, *fullname;
5734     int index;
5735
5736     name = XawDialogGetValueString(w = XtParent(w));
5737
5738     if ((name != NULL) && (*name != NULLCHAR)) {
5739         strcpy(buf, name);
5740         XtPopdown(w = XtParent(XtParent(w)));
5741         XtDestroyWidget(w);
5742         filenameUp = False;
5743
5744         p = strrchr(buf, ' ');
5745         if (p == NULL) {
5746             index = 0;
5747         } else {
5748             *p++ = NULLCHAR;
5749             index = atoi(p);
5750         }
5751         fullname = ExpandPathName(buf);
5752         if (!fullname) {
5753             ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5754         }
5755         else {
5756             f = fopen(fullname, fileOpenMode);
5757             if (f == NULL) {
5758                 DisplayError(_("Failed to open file"), errno);
5759             } else {
5760                 (void) (*fileProc)(f, index, buf);
5761             }
5762         }
5763         ModeHighlight();
5764         return;
5765     }
5766
5767     XtPopdown(w = XtParent(XtParent(w)));
5768     XtDestroyWidget(w);
5769     filenameUp = False;
5770     ModeHighlight();
5771 }
5772
5773 void PromotionPopUp()
5774 {
5775     Arg args[16];
5776     Widget dialog, layout;
5777     Position x, y;
5778     Dimension bw_width, pw_width;
5779     int j;
5780
5781     j = 0;
5782     XtSetArg(args[j], XtNwidth, &bw_width); j++;
5783     XtGetValues(boardWidget, args, j);
5784
5785     j = 0;
5786     XtSetArg(args[j], XtNresizable, True); j++;
5787     XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5788     promotionShell =
5789       XtCreatePopupShell("Promotion", transientShellWidgetClass,
5790                          shellWidget, args, j);
5791     layout =
5792       XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5793                             layoutArgs, XtNumber(layoutArgs));
5794
5795     j = 0;
5796     XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5797     XtSetArg(args[j], XtNborderWidth, 0); j++;
5798     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5799                                    layout, args, j);
5800
5801   if(gameInfo.variant != VariantShogi) {
5802     XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5803                        (XtPointer) dialog);
5804     XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5805                        (XtPointer) dialog);
5806     XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5807                        (XtPointer) dialog);
5808     XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5809                        (XtPointer) dialog);
5810     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5811         gameInfo.variant == VariantGiveaway) {
5812       XawDialogAddButton(dialog, _("King"), PromotionCallback,
5813                          (XtPointer) dialog);
5814     }
5815     if(gameInfo.variant == VariantCapablanca || 
5816        gameInfo.variant == VariantGothic || 
5817        gameInfo.variant == VariantCapaRandom) {
5818       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5819                          (XtPointer) dialog);
5820       XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5821                          (XtPointer) dialog);
5822     }
5823   } else // [HGM] shogi
5824   {
5825       XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5826                          (XtPointer) dialog);
5827       XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5828                          (XtPointer) dialog);
5829   }
5830     XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5831                        (XtPointer) dialog);
5832
5833     XtRealizeWidget(promotionShell);
5834     CatchDeleteWindow(promotionShell, "PromotionPopDown");
5835
5836     j = 0;
5837     XtSetArg(args[j], XtNwidth, &pw_width); j++;
5838     XtGetValues(promotionShell, args, j);
5839
5840     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5841                       lineGap + squareSize/3 +
5842                       ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5843                        0 : 6*(squareSize + lineGap)), &x, &y);
5844
5845     j = 0;
5846     XtSetArg(args[j], XtNx, x); j++;
5847     XtSetArg(args[j], XtNy, y); j++;
5848     XtSetValues(promotionShell, args, j);
5849
5850     XtPopup(promotionShell, XtGrabNone);
5851
5852     promotionUp = True;
5853 }
5854
5855 void PromotionPopDown()
5856 {
5857     if (!promotionUp) return;
5858     XtPopdown(promotionShell);
5859     XtDestroyWidget(promotionShell);
5860     promotionUp = False;
5861 }
5862
5863 void PromotionCallback(w, client_data, call_data)
5864      Widget w;
5865      XtPointer client_data, call_data;
5866 {
5867     String name;
5868     Arg args[16];
5869     int promoChar;
5870
5871     XtSetArg(args[0], XtNlabel, &name);
5872     XtGetValues(w, args, 1);
5873
5874     PromotionPopDown();
5875
5876     if (fromX == -1) return;
5877
5878     if (strcmp(name, _("cancel")) == 0) {
5879         fromX = fromY = -1;
5880         ClearHighlights();
5881         return;
5882     } else if (strcmp(name, _("Knight")) == 0) {
5883         promoChar = 'n';
5884     } else if (strcmp(name, _("Promote")) == 0) {
5885         promoChar = '+';
5886     } else if (strcmp(name, _("Defer")) == 0) {
5887         promoChar = '=';
5888     } else {
5889         promoChar = ToLower(name[0]);
5890     }
5891
5892     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5893
5894     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5895     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5896     fromX = fromY = -1;
5897 }
5898
5899
5900 void ErrorCallback(w, client_data, call_data)
5901      Widget w;
5902      XtPointer client_data, call_data;
5903 {
5904     errorUp = False;
5905     XtPopdown(w = XtParent(XtParent(XtParent(w))));
5906     XtDestroyWidget(w);
5907     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5908 }
5909
5910
5911 void ErrorPopDown()
5912 {
5913     if (!errorUp) return;
5914     errorUp = False;
5915     XtPopdown(errorShell);
5916     XtDestroyWidget(errorShell);
5917     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5918 }
5919
5920 void ErrorPopUp(title, label, modal)
5921      char *title, *label;
5922      int modal;
5923 {
5924     Arg args[16];
5925     Widget dialog, layout;
5926     Position x, y;
5927     int xx, yy;
5928     Window junk;
5929     Dimension bw_width, pw_width;
5930     Dimension pw_height;
5931     int i;
5932
5933     i = 0;
5934     XtSetArg(args[i], XtNresizable, True);  i++;
5935     XtSetArg(args[i], XtNtitle, title); i++;
5936     errorShell =
5937       XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5938                          shellWidget, args, i);
5939     layout =
5940       XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5941                             layoutArgs, XtNumber(layoutArgs));
5942
5943     i = 0;
5944     XtSetArg(args[i], XtNlabel, label); i++;
5945     XtSetArg(args[i], XtNborderWidth, 0); i++;
5946     dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5947                                    layout, args, i);
5948
5949     XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5950
5951     XtRealizeWidget(errorShell);
5952     CatchDeleteWindow(errorShell, "ErrorPopDown");
5953
5954     i = 0;
5955     XtSetArg(args[i], XtNwidth, &bw_width);  i++;
5956     XtGetValues(boardWidget, args, i);
5957     i = 0;
5958     XtSetArg(args[i], XtNwidth, &pw_width);  i++;
5959     XtSetArg(args[i], XtNheight, &pw_height);  i++;
5960     XtGetValues(errorShell, args, i);
5961
5962 #ifdef NOTDEF
5963     /* This code seems to tickle an X bug if it is executed too soon
5964        after xboard starts up.  The coordinates get transformed as if
5965        the main window was positioned at (0, 0).
5966        */
5967     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5968                       0 - pw_height + squareSize / 3, &x, &y);
5969 #else
5970     XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5971                           RootWindowOfScreen(XtScreen(boardWidget)),
5972                           (bw_width - pw_width) / 2,
5973                           0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5974     x = xx;
5975     y = yy;
5976 #endif
5977     if (y < 0) y = 0; /*avoid positioning top offscreen*/
5978
5979     i = 0;
5980     XtSetArg(args[i], XtNx, x);  i++;
5981     XtSetArg(args[i], XtNy, y);  i++;
5982     XtSetValues(errorShell, args, i);
5983
5984     errorUp = True;
5985     XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5986 }
5987
5988 /* Disable all user input other than deleting the window */
5989 static int frozen = 0;
5990 void FreezeUI()
5991 {
5992   if (frozen) return;
5993   /* Grab by a widget that doesn't accept input */
5994   XtAddGrab(messageWidget, TRUE, FALSE);
5995   frozen = 1;
5996 }
5997
5998 /* Undo a FreezeUI */
5999 void ThawUI()
6000 {
6001   if (!frozen) return;
6002   XtRemoveGrab(messageWidget);
6003   frozen = 0;
6004 }
6005
6006 char *ModeToWidgetName(mode)
6007      GameMode mode;
6008 {
6009     switch (mode) {
6010       case BeginningOfGame:
6011         if (appData.icsActive)
6012           return "menuMode.ICS Client";
6013         else if (appData.noChessProgram ||
6014                  *appData.cmailGameName != NULLCHAR)
6015           return "menuMode.Edit Game";
6016         else
6017           return "menuMode.Machine Black";
6018       case MachinePlaysBlack:
6019         return "menuMode.Machine Black";
6020       case MachinePlaysWhite:
6021         return "menuMode.Machine White";
6022       case AnalyzeMode:
6023         return "menuMode.Analysis Mode";
6024       case AnalyzeFile:
6025         return "menuMode.Analyze File";
6026       case TwoMachinesPlay:
6027         return "menuMode.Two Machines";
6028       case EditGame:
6029         return "menuMode.Edit Game";
6030       case PlayFromGameFile:
6031         return "menuFile.Load Game";
6032       case EditPosition:
6033         return "menuMode.Edit Position";
6034       case Training:
6035         return "menuMode.Training";
6036       case IcsPlayingWhite:
6037       case IcsPlayingBlack:
6038       case IcsObserving:
6039       case IcsIdle:
6040       case IcsExamining:
6041         return "menuMode.ICS Client";
6042       default:
6043       case EndOfGame:
6044         return NULL;
6045     }
6046 }
6047
6048 void ModeHighlight()
6049 {
6050     Arg args[16];
6051     static int oldPausing = FALSE;
6052     static GameMode oldmode = (GameMode) -1;
6053     char *wname;
6054
6055     if (!boardWidget || !XtIsRealized(boardWidget)) return;
6056
6057     if (pausing != oldPausing) {
6058         oldPausing = pausing;
6059         if (pausing) {
6060             XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6061         } else {
6062             XtSetArg(args[0], XtNleftBitmap, None);
6063         }
6064         XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
6065                     args, 1);
6066
6067         if (appData.showButtonBar) {
6068           /* Always toggle, don't set.  Previous code messes up when
6069              invoked while the button is pressed, as releasing it
6070              toggles the state again. */
6071           {
6072             Pixel oldbg, oldfg;
6073             XtSetArg(args[0], XtNbackground, &oldbg);
6074             XtSetArg(args[1], XtNforeground, &oldfg);
6075             XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
6076                         args, 2);
6077             XtSetArg(args[0], XtNbackground, oldfg);
6078             XtSetArg(args[1], XtNforeground, oldbg);
6079           }
6080           XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
6081         }
6082     }
6083
6084     wname = ModeToWidgetName(oldmode);
6085     if (wname != NULL) {
6086         XtSetArg(args[0], XtNleftBitmap, None);
6087         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
6088     }
6089     wname = ModeToWidgetName(gameMode);
6090     if (wname != NULL) {
6091         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6092         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
6093     }
6094     oldmode = gameMode;
6095
6096     /* Maybe all the enables should be handled here, not just this one */
6097     XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
6098                    gameMode == Training || gameMode == PlayFromGameFile);
6099 }
6100
6101
6102 /*
6103  * Button/menu procedures
6104  */
6105 void ResetProc(w, event, prms, nprms)
6106      Widget w;
6107      XEvent *event;
6108      String *prms;
6109      Cardinal *nprms;
6110 {
6111     ResetGameEvent();
6112 }
6113
6114 int LoadGamePopUp(f, gameNumber, title)
6115      FILE *f;
6116      int gameNumber;
6117      char *title;
6118 {
6119     cmailMsgLoaded = FALSE;
6120     if (gameNumber == 0) {
6121         int error = GameListBuild(f);
6122         if (error) {
6123             DisplayError(_("Cannot build game list"), error);
6124         } else if (!ListEmpty(&gameList) &&
6125                    ((ListGame *) gameList.tailPred)->number > 1) {
6126             GameListPopUp(f, title);
6127             return TRUE;
6128         }
6129         GameListDestroy();
6130         gameNumber = 1;
6131     }
6132     return LoadGame(f, gameNumber, title, FALSE);
6133 }
6134
6135 void LoadGameProc(w, event, prms, nprms)
6136      Widget w;
6137      XEvent *event;
6138      String *prms;
6139      Cardinal *nprms;
6140 {
6141     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
6142         Reset(FALSE, TRUE);
6143     }
6144     FileNamePopUp(_("Load game file name?"), "", LoadGamePopUp, "rb");
6145 }
6146
6147 void LoadNextGameProc(w, event, prms, nprms)
6148      Widget w;
6149      XEvent *event;
6150      String *prms;
6151      Cardinal *nprms;
6152 {
6153     ReloadGame(1);
6154 }
6155
6156 void LoadPrevGameProc(w, event, prms, nprms)
6157      Widget w;
6158      XEvent *event;
6159      String *prms;
6160      Cardinal *nprms;
6161 {
6162     ReloadGame(-1);
6163 }
6164
6165 void ReloadGameProc(w, event, prms, nprms)
6166      Widget w;
6167      XEvent *event;
6168      String *prms;
6169      Cardinal *nprms;
6170 {
6171     ReloadGame(0);
6172 }
6173
6174 void LoadNextPositionProc(w, event, prms, nprms)
6175      Widget w;
6176      XEvent *event;
6177      String *prms;
6178      Cardinal *nprms;
6179 {
6180     ReloadPosition(1);
6181 }
6182
6183 void LoadPrevPositionProc(w, event, prms, nprms)
6184      Widget w;
6185      XEvent *event;
6186      String *prms;
6187      Cardinal *nprms;
6188 {
6189     ReloadPosition(-1);
6190 }
6191
6192 void ReloadPositionProc(w, event, prms, nprms)
6193      Widget w;
6194      XEvent *event;
6195      String *prms;
6196      Cardinal *nprms;
6197 {
6198     ReloadPosition(0);
6199 }
6200
6201 void LoadPositionProc(w, event, prms, nprms)
6202      Widget w;
6203      XEvent *event;
6204      String *prms;
6205      Cardinal *nprms;
6206 {
6207     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
6208         Reset(FALSE, TRUE);
6209     }
6210     FileNamePopUp(_("Load position file name?"), "", LoadPosition, "rb");
6211 }
6212
6213 void SaveGameProc(w, event, prms, nprms)
6214      Widget w;
6215      XEvent *event;
6216      String *prms;
6217      Cardinal *nprms;
6218 {
6219     FileNamePopUp(_("Save game file name?"),
6220                   DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
6221                   SaveGame, "a");
6222 }
6223
6224 void SavePositionProc(w, event, prms, nprms)
6225      Widget w;
6226      XEvent *event;
6227      String *prms;
6228      Cardinal *nprms;
6229 {
6230     FileNamePopUp(_("Save position file name?"),
6231                   DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
6232                   SavePosition, "a");
6233 }
6234
6235 void ReloadCmailMsgProc(w, event, prms, nprms)
6236      Widget w;
6237      XEvent *event;
6238      String *prms;
6239      Cardinal *nprms;
6240 {
6241     ReloadCmailMsgEvent(FALSE);
6242 }
6243
6244 void MailMoveProc(w, event, prms, nprms)
6245      Widget w;
6246      XEvent *event;
6247      String *prms;
6248      Cardinal *nprms;
6249 {
6250     MailMoveEvent();
6251 }
6252
6253 /* this variable is shared between CopyPositionProc and SendPositionSelection */
6254 static char *selected_fen_position=NULL;
6255
6256 static Boolean
6257 SendPositionSelection(Widget w, Atom *selection, Atom *target,
6258                  Atom *type_return, XtPointer *value_return,
6259                  unsigned long *length_return, int *format_return)
6260 {
6261   char *selection_tmp;
6262
6263   if (!selected_fen_position) return False; /* should never happen */
6264   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
6265     /* note: since no XtSelectionDoneProc was registered, Xt will
6266      * automatically call XtFree on the value returned.  So have to
6267      * make a copy of it allocated with XtMalloc */
6268     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
6269     strcpy(selection_tmp, selected_fen_position);
6270
6271     *value_return=selection_tmp;
6272     *length_return=strlen(selection_tmp);
6273     *type_return=*target;
6274     *format_return = 8; /* bits per byte */
6275     return True;
6276   } else if (*target == XA_TARGETS(xDisplay)) {
6277     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
6278     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
6279     targets_tmp[1] = XA_STRING;
6280     *value_return = targets_tmp;
6281     *type_return = XA_ATOM;
6282     *length_return = 2;
6283     *format_return = 8 * sizeof(Atom);
6284     if (*format_return > 32) {
6285       *length_return *= *format_return / 32;
6286       *format_return = 32;
6287     }
6288     return True;
6289   } else {
6290     return False;
6291   }
6292 }
6293
6294 /* note: when called from menu all parameters are NULL, so no clue what the
6295  * Widget which was clicked on was, or what the click event was
6296  */
6297 void CopyPositionProc(w, event, prms, nprms)
6298   Widget w;
6299   XEvent *event;
6300   String *prms;
6301   Cardinal *nprms;
6302   {
6303     /*
6304      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
6305      * have a notion of a position that is selected but not copied.
6306      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
6307      */
6308     if(gameMode == EditPosition) EditPositionDone(TRUE);
6309     if (selected_fen_position) free(selected_fen_position);
6310     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
6311     if (!selected_fen_position) return;
6312     XtOwnSelection(menuBarWidget, XA_PRIMARY,
6313                    CurrentTime,
6314                    SendPositionSelection,
6315                    NULL/* lose_ownership_proc */ ,
6316                    NULL/* transfer_done_proc */);
6317     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
6318                    CurrentTime,
6319                    SendPositionSelection,
6320                    NULL/* lose_ownership_proc */ ,
6321                    NULL/* transfer_done_proc */);
6322   }
6323
6324 /* function called when the data to Paste is ready */
6325 static void
6326 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
6327            Atom *type, XtPointer value, unsigned long *len, int *format)
6328 {
6329   char *fenstr=value;
6330   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
6331   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
6332   EditPositionPasteFEN(fenstr);
6333   XtFree(value);
6334 }
6335
6336 /* called when Paste Position button is pressed,
6337  * all parameters will be NULL */
6338 void PastePositionProc(w, event, prms, nprms)
6339   Widget w;
6340   XEvent *event;
6341   String *prms;
6342   Cardinal *nprms;
6343 {
6344     XtGetSelectionValue(menuBarWidget, 
6345       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
6346       /* (XtSelectionCallbackProc) */ PastePositionCB,
6347       NULL, /* client_data passed to PastePositionCB */
6348
6349       /* better to use the time field from the event that triggered the
6350        * call to this function, but that isn't trivial to get
6351        */
6352       CurrentTime
6353     );
6354     return;
6355 }
6356
6357 static Boolean
6358 SendGameSelection(Widget w, Atom *selection, Atom *target,
6359                   Atom *type_return, XtPointer *value_return,
6360                   unsigned long *length_return, int *format_return)
6361 {
6362   char *selection_tmp;
6363
6364   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
6365     FILE* f = fopen(gameCopyFilename, "r");
6366     long len;
6367     size_t count;
6368     if (f == NULL) return False;
6369     fseek(f, 0, 2);
6370     len = ftell(f);
6371     rewind(f);
6372     selection_tmp = XtMalloc(len + 1);
6373     count = fread(selection_tmp, 1, len, f);
6374     if (len != count) {
6375       XtFree(selection_tmp);
6376       return False;
6377     }
6378     selection_tmp[len] = NULLCHAR;
6379     *value_return = selection_tmp;
6380     *length_return = len;
6381     *type_return = *target;
6382     *format_return = 8; /* bits per byte */
6383     return True;
6384   } else if (*target == XA_TARGETS(xDisplay)) {
6385     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
6386     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
6387     targets_tmp[1] = XA_STRING;
6388     *value_return = targets_tmp;
6389     *type_return = XA_ATOM;
6390     *length_return = 2;
6391     *format_return = 8 * sizeof(Atom);
6392     if (*format_return > 32) {
6393       *length_return *= *format_return / 32;
6394       *format_return = 32;
6395     }
6396     return True;
6397   } else {
6398     return False;
6399   }
6400 }
6401
6402 /* note: when called from menu all parameters are NULL, so no clue what the
6403  * Widget which was clicked on was, or what the click event was
6404  */
6405 void CopyGameProc(w, event, prms, nprms)
6406   Widget w;
6407   XEvent *event;
6408   String *prms;
6409   Cardinal *nprms;
6410 {
6411   int ret;
6412
6413   ret = SaveGameToFile(gameCopyFilename, FALSE);
6414   if (!ret) return;
6415
6416   /*
6417    * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
6418    * have a notion of a game that is selected but not copied.
6419    * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
6420    */
6421   XtOwnSelection(menuBarWidget, XA_PRIMARY,
6422                  CurrentTime,
6423                  SendGameSelection,
6424                  NULL/* lose_ownership_proc */ ,
6425                  NULL/* transfer_done_proc */);
6426   XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
6427                  CurrentTime,
6428                  SendGameSelection,
6429                  NULL/* lose_ownership_proc */ ,
6430                  NULL/* transfer_done_proc */);
6431 }
6432
6433 /* function called when the data to Paste is ready */
6434 static void
6435 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
6436             Atom *type, XtPointer value, unsigned long *len, int *format)
6437 {
6438   FILE* f;
6439   if (value == NULL || *len == 0) {
6440     return; /* nothing had been selected to copy */
6441   }
6442   f = fopen(gamePasteFilename, "w");
6443   if (f == NULL) {
6444     DisplayError(_("Can't open temp file"), errno);
6445     return;
6446   }
6447   fwrite(value, 1, *len, f);
6448   fclose(f);
6449   XtFree(value);
6450   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
6451 }
6452
6453 /* called when Paste Game button is pressed,
6454  * all parameters will be NULL */
6455 void PasteGameProc(w, event, prms, nprms)
6456   Widget w;
6457   XEvent *event;
6458   String *prms;
6459   Cardinal *nprms;
6460 {
6461     XtGetSelectionValue(menuBarWidget,
6462       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
6463       /* (XtSelectionCallbackProc) */ PasteGameCB,
6464       NULL, /* client_data passed to PasteGameCB */
6465
6466       /* better to use the time field from the event that triggered the
6467        * call to this function, but that isn't trivial to get
6468        */
6469       CurrentTime
6470     );
6471     return;
6472 }
6473
6474
6475 void AutoSaveGame()
6476 {
6477     SaveGameProc(NULL, NULL, NULL, NULL);
6478 }
6479
6480
6481 void QuitProc(w, event, prms, nprms)
6482      Widget w;
6483      XEvent *event;
6484      String *prms;
6485      Cardinal *nprms;
6486 {
6487     ExitEvent(0);
6488 }
6489
6490 void PauseProc(w, event, prms, nprms)
6491      Widget w;
6492      XEvent *event;
6493      String *prms;
6494      Cardinal *nprms;
6495 {
6496     PauseEvent();
6497 }
6498
6499
6500 void MachineBlackProc(w, event, prms, nprms)
6501      Widget w;
6502      XEvent *event;
6503      String *prms;
6504      Cardinal *nprms;
6505 {
6506     MachineBlackEvent();
6507 }
6508
6509 void MachineWhiteProc(w, event, prms, nprms)
6510      Widget w;
6511      XEvent *event;
6512      String *prms;
6513      Cardinal *nprms;
6514 {
6515     MachineWhiteEvent();
6516 }
6517
6518 void AnalyzeModeProc(w, event, prms, nprms)
6519      Widget w;
6520      XEvent *event;
6521      String *prms;
6522      Cardinal *nprms;
6523 {
6524     char buf[MSG_SIZ];
6525
6526     if (!first.analysisSupport) {
6527       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6528       DisplayError(buf, 0);
6529       return;
6530     }
6531     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
6532     if (appData.icsActive) {
6533         if (gameMode != IcsObserving) {
6534             sprintf(buf,_("You are not observing a game"));
6535             DisplayError(buf, 0);
6536             /* secure check */
6537             if (appData.icsEngineAnalyze) {
6538                 if (appData.debugMode)
6539                     fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
6540                 ExitAnalyzeMode();
6541                 ModeHighlight();
6542             }
6543             return;
6544         }
6545         /* if enable, use want disable icsEngineAnalyze */
6546         if (appData.icsEngineAnalyze) {
6547                 ExitAnalyzeMode();
6548                 ModeHighlight();
6549                 return;
6550         }
6551         appData.icsEngineAnalyze = TRUE;
6552         if (appData.debugMode)
6553             fprintf(debugFP, _("ICS engine analyze starting... \n"));
6554     }
6555     if (!appData.showThinking)
6556       ShowThinkingProc(w,event,prms,nprms);
6557
6558     AnalyzeModeEvent();
6559 }
6560
6561 void AnalyzeFileProc(w, event, prms, nprms)
6562      Widget w;
6563      XEvent *event;
6564      String *prms;
6565      Cardinal *nprms;
6566 {
6567     if (!first.analysisSupport) {
6568       char buf[MSG_SIZ];
6569       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6570       DisplayError(buf, 0);
6571       return;
6572     }
6573     Reset(FALSE, TRUE);
6574
6575     if (!appData.showThinking)
6576       ShowThinkingProc(w,event,prms,nprms);
6577
6578     AnalyzeFileEvent();
6579     FileNamePopUp(_("File to analyze"), "", LoadGamePopUp, "rb");
6580     AnalysisPeriodicEvent(1);
6581 }
6582
6583 void TwoMachinesProc(w, event, prms, nprms)
6584      Widget w;
6585      XEvent *event;
6586      String *prms;
6587      Cardinal *nprms;
6588 {
6589     TwoMachinesEvent();
6590 }
6591
6592 void IcsClientProc(w, event, prms, nprms)
6593      Widget w;
6594      XEvent *event;
6595      String *prms;
6596      Cardinal *nprms;
6597 {
6598     IcsClientEvent();
6599 }
6600
6601 void EditGameProc(w, event, prms, nprms)
6602      Widget w;
6603      XEvent *event;
6604      String *prms;
6605      Cardinal *nprms;
6606 {
6607     EditGameEvent();
6608 }
6609
6610 void EditPositionProc(w, event, prms, nprms)
6611      Widget w;
6612      XEvent *event;
6613      String *prms;
6614      Cardinal *nprms;
6615 {
6616     EditPositionEvent();
6617 }
6618
6619 void TrainingProc(w, event, prms, nprms)
6620      Widget w;
6621      XEvent *event;
6622      String *prms;
6623      Cardinal *nprms;
6624 {
6625     TrainingEvent();
6626 }
6627
6628 void EditCommentProc(w, event, prms, nprms)
6629      Widget w;
6630      XEvent *event;
6631      String *prms;
6632      Cardinal *nprms;
6633 {
6634     if (editUp) {
6635         EditCommentPopDown();
6636     } else {
6637         EditCommentEvent();
6638     }
6639 }
6640
6641 void IcsInputBoxProc(w, event, prms, nprms)
6642      Widget w;
6643      XEvent *event;
6644      String *prms;
6645      Cardinal *nprms;
6646 {
6647     if (ICSInputBoxUp) {
6648         ICSInputBoxPopDown();
6649     } else {
6650         ICSInputBoxPopUp();
6651     }
6652 }
6653
6654 void AcceptProc(w, event, prms, nprms)
6655      Widget w;
6656      XEvent *event;
6657      String *prms;
6658      Cardinal *nprms;
6659 {
6660     AcceptEvent();
6661 }
6662
6663 void DeclineProc(w, event, prms, nprms)
6664      Widget w;
6665      XEvent *event;
6666      String *prms;
6667      Cardinal *nprms;
6668 {
6669     DeclineEvent();
6670 }
6671
6672 void RematchProc(w, event, prms, nprms)
6673      Widget w;
6674      XEvent *event;
6675      String *prms;
6676      Cardinal *nprms;
6677 {
6678     RematchEvent();
6679 }
6680
6681 void CallFlagProc(w, event, prms, nprms)
6682      Widget w;
6683      XEvent *event;
6684      String *prms;
6685      Cardinal *nprms;
6686 {
6687     CallFlagEvent();
6688 }
6689
6690 void DrawProc(w, event, prms, nprms)
6691      Widget w;
6692      XEvent *event;
6693      String *prms;
6694      Cardinal *nprms;
6695 {
6696     DrawEvent();
6697 }
6698
6699 void AbortProc(w, event, prms, nprms)
6700      Widget w;
6701      XEvent *event;
6702      String *prms;
6703      Cardinal *nprms;
6704 {
6705     AbortEvent();
6706 }
6707
6708 void AdjournProc(w, event, prms, nprms)
6709      Widget w;
6710      XEvent *event;
6711      String *prms;
6712      Cardinal *nprms;
6713 {
6714     AdjournEvent();
6715 }
6716
6717 void ResignProc(w, event, prms, nprms)
6718      Widget w;
6719      XEvent *event;
6720      String *prms;
6721      Cardinal *nprms;
6722 {
6723     ResignEvent();
6724 }
6725
6726 void AdjuWhiteProc(w, event, prms, nprms)
6727      Widget w;
6728      XEvent *event;
6729      String *prms;
6730      Cardinal *nprms;
6731 {
6732     UserAdjudicationEvent(+1);
6733 }
6734
6735 void AdjuBlackProc(w, event, prms, nprms)
6736      Widget w;
6737      XEvent *event;
6738      String *prms;
6739      Cardinal *nprms;
6740 {
6741     UserAdjudicationEvent(-1);
6742 }
6743
6744 void AdjuDrawProc(w, event, prms, nprms)
6745      Widget w;
6746      XEvent *event;
6747      String *prms;
6748      Cardinal *nprms;
6749 {
6750     UserAdjudicationEvent(0);
6751 }
6752
6753 void EnterKeyProc(w, event, prms, nprms)
6754      Widget w;
6755      XEvent *event;
6756      String *prms;
6757      Cardinal *nprms;
6758 {
6759     if (ICSInputBoxUp == True)
6760       ICSInputSendText();
6761 }
6762
6763 void StopObservingProc(w, event, prms, nprms)
6764      Widget w;
6765      XEvent *event;
6766      String *prms;
6767      Cardinal *nprms;
6768 {
6769     StopObservingEvent();
6770 }
6771
6772 void StopExaminingProc(w, event, prms, nprms)
6773      Widget w;
6774      XEvent *event;
6775      String *prms;
6776      Cardinal *nprms;
6777 {
6778     StopExaminingEvent();
6779 }
6780
6781
6782 void ForwardProc(w, event, prms, nprms)
6783      Widget w;
6784      XEvent *event;
6785      String *prms;
6786      Cardinal *nprms;
6787 {
6788     ForwardEvent();
6789 }
6790
6791
6792 void BackwardProc(w, event, prms, nprms)
6793      Widget w;
6794      XEvent *event;
6795      String *prms;
6796      Cardinal *nprms;
6797 {
6798     BackwardEvent();
6799 }
6800
6801 void ToStartProc(w, event, prms, nprms)
6802      Widget w;
6803      XEvent *event;
6804      String *prms;
6805      Cardinal *nprms;
6806 {
6807     ToStartEvent();
6808 }
6809
6810 void ToEndProc(w, event, prms, nprms)
6811      Widget w;
6812      XEvent *event;
6813      String *prms;
6814      Cardinal *nprms;
6815 {
6816     ToEndEvent();
6817 }
6818
6819 void RevertProc(w, event, prms, nprms)
6820      Widget w;
6821      XEvent *event;
6822      String *prms;
6823      Cardinal *nprms;
6824 {
6825     RevertEvent();
6826 }
6827
6828 void TruncateGameProc(w, event, prms, nprms)
6829      Widget w;
6830      XEvent *event;
6831      String *prms;
6832      Cardinal *nprms;
6833 {
6834     TruncateGameEvent();
6835 }
6836 void RetractMoveProc(w, event, prms, nprms)
6837      Widget w;
6838      XEvent *event;
6839      String *prms;
6840      Cardinal *nprms;
6841 {
6842     RetractMoveEvent();
6843 }
6844
6845 void MoveNowProc(w, event, prms, nprms)
6846      Widget w;
6847      XEvent *event;
6848      String *prms;
6849      Cardinal *nprms;
6850 {
6851     MoveNowEvent();
6852 }
6853
6854
6855 void AlwaysQueenProc(w, event, prms, nprms)
6856      Widget w;
6857      XEvent *event;
6858      String *prms;
6859      Cardinal *nprms;
6860 {
6861     Arg args[16];
6862
6863     appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6864
6865     if (appData.alwaysPromoteToQueen) {
6866         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6867     } else {
6868         XtSetArg(args[0], XtNleftBitmap, None);
6869     }
6870     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6871                 args, 1);
6872 }
6873
6874 void AnimateDraggingProc(w, event, prms, nprms)
6875      Widget w;
6876      XEvent *event;
6877      String *prms;
6878      Cardinal *nprms;
6879 {
6880     Arg args[16];
6881
6882     appData.animateDragging = !appData.animateDragging;
6883
6884     if (appData.animateDragging) {
6885         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6886         CreateAnimVars();
6887     } else {
6888         XtSetArg(args[0], XtNleftBitmap, None);
6889     }
6890     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6891                 args, 1);
6892 }
6893
6894 void AnimateMovingProc(w, event, prms, nprms)
6895      Widget w;
6896      XEvent *event;
6897      String *prms;
6898      Cardinal *nprms;
6899 {
6900     Arg args[16];
6901
6902     appData.animate = !appData.animate;
6903
6904     if (appData.animate) {
6905         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6906         CreateAnimVars();
6907     } else {
6908         XtSetArg(args[0], XtNleftBitmap, None);
6909     }
6910     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6911                 args, 1);
6912 }
6913
6914 void AutocommProc(w, event, prms, nprms)
6915      Widget w;
6916      XEvent *event;
6917      String *prms;
6918      Cardinal *nprms;
6919 {
6920     Arg args[16];
6921
6922     appData.autoComment = !appData.autoComment;
6923
6924     if (appData.autoComment) {
6925         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6926     } else {
6927         XtSetArg(args[0], XtNleftBitmap, None);
6928     }
6929     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
6930                 args, 1);
6931 }
6932
6933
6934 void AutoflagProc(w, event, prms, nprms)
6935      Widget w;
6936      XEvent *event;
6937      String *prms;
6938      Cardinal *nprms;
6939 {
6940     Arg args[16];
6941
6942     appData.autoCallFlag = !appData.autoCallFlag;
6943
6944     if (appData.autoCallFlag) {
6945         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6946     } else {
6947         XtSetArg(args[0], XtNleftBitmap, None);
6948     }
6949     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6950                 args, 1);
6951 }
6952
6953 void AutoflipProc(w, event, prms, nprms)
6954      Widget w;
6955      XEvent *event;
6956      String *prms;
6957      Cardinal *nprms;
6958 {
6959     Arg args[16];
6960
6961     appData.autoFlipView = !appData.autoFlipView;
6962
6963     if (appData.autoFlipView) {
6964         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6965     } else {
6966         XtSetArg(args[0], XtNleftBitmap, None);
6967     }
6968     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6969                 args, 1);
6970 }
6971
6972 void AutobsProc(w, event, prms, nprms)
6973      Widget w;
6974      XEvent *event;
6975      String *prms;
6976      Cardinal *nprms;
6977 {
6978     Arg args[16];
6979
6980     appData.autoObserve = !appData.autoObserve;
6981
6982     if (appData.autoObserve) {
6983         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6984     } else {
6985         XtSetArg(args[0], XtNleftBitmap, None);
6986     }
6987     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
6988                 args, 1);
6989 }
6990
6991 void AutoraiseProc(w, event, prms, nprms)
6992      Widget w;
6993      XEvent *event;
6994      String *prms;
6995      Cardinal *nprms;
6996 {
6997     Arg args[16];
6998
6999     appData.autoRaiseBoard = !appData.autoRaiseBoard;
7000
7001     if (appData.autoRaiseBoard) {
7002         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7003     } else {
7004         XtSetArg(args[0], XtNleftBitmap, None);
7005     }
7006     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Raise Board"),
7007                 args, 1);
7008 }
7009
7010 void AutosaveProc(w, event, prms, nprms)
7011      Widget w;
7012      XEvent *event;
7013      String *prms;
7014      Cardinal *nprms;
7015 {
7016     Arg args[16];
7017
7018     appData.autoSaveGames = !appData.autoSaveGames;
7019
7020     if (appData.autoSaveGames) {
7021         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7022     } else {
7023         XtSetArg(args[0], XtNleftBitmap, None);
7024     }
7025     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
7026                 args, 1);
7027 }
7028
7029 void BlindfoldProc(w, event, prms, nprms)
7030      Widget w;
7031      XEvent *event;
7032      String *prms;
7033      Cardinal *nprms;
7034 {
7035     Arg args[16];
7036
7037     appData.blindfold = !appData.blindfold;
7038
7039     if (appData.blindfold) {
7040         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7041     } else {
7042         XtSetArg(args[0], XtNleftBitmap, None);
7043     }
7044     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
7045                 args, 1);
7046
7047     DrawPosition(True, NULL);
7048 }
7049
7050 void TestLegalityProc(w, event, prms, nprms)
7051      Widget w;
7052      XEvent *event;
7053      String *prms;
7054      Cardinal *nprms;
7055 {
7056     Arg args[16];
7057
7058     appData.testLegality = !appData.testLegality;
7059
7060     if (appData.testLegality) {
7061         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7062     } else {
7063         XtSetArg(args[0], XtNleftBitmap, None);
7064     }
7065     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
7066                 args, 1);
7067 }
7068
7069
7070 void FlashMovesProc(w, event, prms, nprms)
7071      Widget w;
7072      XEvent *event;
7073      String *prms;
7074      Cardinal *nprms;
7075 {
7076     Arg args[16];
7077
7078     if (appData.flashCount == 0) {
7079         appData.flashCount = 3;
7080     } else {
7081         appData.flashCount = -appData.flashCount;
7082     }
7083
7084     if (appData.flashCount > 0) {
7085         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7086     } else {
7087         XtSetArg(args[0], XtNleftBitmap, None);
7088     }
7089     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
7090                 args, 1);
7091 }
7092
7093 void FlipViewProc(w, event, prms, nprms)
7094      Widget w;
7095      XEvent *event;
7096      String *prms;
7097      Cardinal *nprms;
7098 {
7099     flipView = !flipView;
7100     DrawPosition(True, NULL);
7101 }
7102
7103 void GetMoveListProc(w, event, prms, nprms)
7104      Widget w;
7105      XEvent *event;
7106      String *prms;
7107      Cardinal *nprms;
7108 {
7109     Arg args[16];
7110
7111     appData.getMoveList = !appData.getMoveList;
7112
7113     if (appData.getMoveList) {
7114         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7115         GetMoveListEvent();
7116     } else {
7117         XtSetArg(args[0], XtNleftBitmap, None);
7118     }
7119     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
7120                 args, 1);
7121 }
7122
7123 #if HIGHDRAG
7124 void HighlightDraggingProc(w, event, prms, nprms)
7125      Widget w;
7126      XEvent *event;
7127      String *prms;
7128      Cardinal *nprms;
7129 {
7130     Arg args[16];
7131
7132     appData.highlightDragging = !appData.highlightDragging;
7133
7134     if (appData.highlightDragging) {
7135         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7136     } else {
7137         XtSetArg(args[0], XtNleftBitmap, None);
7138     }
7139     XtSetValues(XtNameToWidget(menuBarWidget,
7140                                "menuOptions.Highlight Dragging"), args, 1);
7141 }
7142 #endif
7143
7144 void HighlightLastMoveProc(w, event, prms, nprms)
7145      Widget w;
7146      XEvent *event;
7147      String *prms;
7148      Cardinal *nprms;
7149 {
7150     Arg args[16];
7151
7152     appData.highlightLastMove = !appData.highlightLastMove;
7153
7154     if (appData.highlightLastMove) {
7155         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7156     } else {
7157         XtSetArg(args[0], XtNleftBitmap, None);
7158     }
7159     XtSetValues(XtNameToWidget(menuBarWidget,
7160                                "menuOptions.Highlight Last Move"), args, 1);
7161 }
7162
7163 void IcsAlarmProc(w, event, prms, nprms)
7164      Widget w;
7165      XEvent *event;
7166      String *prms;
7167      Cardinal *nprms;
7168 {
7169     Arg args[16];
7170
7171     appData.icsAlarm = !appData.icsAlarm;
7172
7173     if (appData.icsAlarm) {
7174         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7175     } else {
7176         XtSetArg(args[0], XtNleftBitmap, None);
7177     }
7178     XtSetValues(XtNameToWidget(menuBarWidget,
7179                                "menuOptions.ICS Alarm"), args, 1);
7180 }
7181
7182 void MoveSoundProc(w, event, prms, nprms)
7183      Widget w;
7184      XEvent *event;
7185      String *prms;
7186      Cardinal *nprms;
7187 {
7188     Arg args[16];
7189
7190     appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
7191
7192     if (appData.ringBellAfterMoves) {
7193         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7194     } else {
7195         XtSetArg(args[0], XtNleftBitmap, None);
7196     }
7197     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
7198                 args, 1);
7199 }
7200
7201
7202 void OldSaveStyleProc(w, event, prms, nprms)
7203      Widget w;
7204      XEvent *event;
7205      String *prms;
7206      Cardinal *nprms;
7207 {
7208     Arg args[16];
7209
7210     appData.oldSaveStyle = !appData.oldSaveStyle;
7211
7212     if (appData.oldSaveStyle) {
7213         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7214     } else {
7215         XtSetArg(args[0], XtNleftBitmap, None);
7216     }
7217     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Old Save Style"),
7218                 args, 1);
7219 }
7220
7221 void PeriodicUpdatesProc(w, event, prms, nprms)
7222      Widget w;
7223      XEvent *event;
7224      String *prms;
7225      Cardinal *nprms;
7226 {
7227     Arg args[16];
7228
7229     PeriodicUpdatesEvent(!appData.periodicUpdates);
7230
7231     if (appData.periodicUpdates) {
7232         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7233     } else {
7234         XtSetArg(args[0], XtNleftBitmap, None);
7235     }
7236     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
7237                 args, 1);
7238 }
7239
7240 void PonderNextMoveProc(w, event, prms, nprms)
7241      Widget w;
7242      XEvent *event;
7243      String *prms;
7244      Cardinal *nprms;
7245 {
7246     Arg args[16];
7247
7248     PonderNextMoveEvent(!appData.ponderNextMove);
7249
7250     if (appData.ponderNextMove) {
7251         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7252     } else {
7253         XtSetArg(args[0], XtNleftBitmap, None);
7254     }
7255     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
7256                 args, 1);
7257 }
7258
7259 void PopupExitMessageProc(w, event, prms, nprms)
7260      Widget w;
7261      XEvent *event;
7262      String *prms;
7263      Cardinal *nprms;
7264 {
7265     Arg args[16];
7266
7267     appData.popupExitMessage = !appData.popupExitMessage;
7268
7269     if (appData.popupExitMessage) {
7270         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7271     } else {
7272         XtSetArg(args[0], XtNleftBitmap, None);
7273     }
7274     XtSetValues(XtNameToWidget(menuBarWidget,
7275                                "menuOptions.Popup Exit Message"), args, 1);
7276 }
7277
7278 void PopupMoveErrorsProc(w, event, prms, nprms)
7279      Widget w;
7280      XEvent *event;
7281      String *prms;
7282      Cardinal *nprms;
7283 {
7284     Arg args[16];
7285
7286     appData.popupMoveErrors = !appData.popupMoveErrors;
7287
7288     if (appData.popupMoveErrors) {
7289         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7290     } else {
7291         XtSetArg(args[0], XtNleftBitmap, None);
7292     }
7293     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
7294                 args, 1);
7295 }
7296
7297 void PremoveProc(w, event, prms, nprms)
7298      Widget w;
7299      XEvent *event;
7300      String *prms;
7301      Cardinal *nprms;
7302 {
7303     Arg args[16];
7304
7305     appData.premove = !appData.premove;
7306
7307     if (appData.premove) {
7308         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7309     } else {
7310         XtSetArg(args[0], XtNleftBitmap, None);
7311     }
7312     XtSetValues(XtNameToWidget(menuBarWidget,
7313                                "menuOptions.Premove"), args, 1);
7314 }
7315
7316 void QuietPlayProc(w, event, prms, nprms)
7317      Widget w;
7318      XEvent *event;
7319      String *prms;
7320      Cardinal *nprms;
7321 {
7322     Arg args[16];
7323
7324     appData.quietPlay = !appData.quietPlay;
7325
7326     if (appData.quietPlay) {
7327         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7328     } else {
7329         XtSetArg(args[0], XtNleftBitmap, None);
7330     }
7331     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Quiet Play"),
7332                 args, 1);
7333 }
7334
7335 void ShowCoordsProc(w, event, prms, nprms)
7336      Widget w;
7337      XEvent *event;
7338      String *prms;
7339      Cardinal *nprms;
7340 {
7341     Arg args[16];
7342
7343     appData.showCoords = !appData.showCoords;
7344
7345     if (appData.showCoords) {
7346         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7347     } else {
7348         XtSetArg(args[0], XtNleftBitmap, None);
7349     }
7350     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
7351                 args, 1);
7352
7353     DrawPosition(True, NULL);
7354 }
7355
7356 void ShowThinkingProc(w, event, prms, nprms)
7357      Widget w;
7358      XEvent *event;
7359      String *prms;
7360      Cardinal *nprms;
7361 {
7362     appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
7363     ShowThinkingEvent();
7364 }
7365
7366 void HideThinkingProc(w, event, prms, nprms)
7367      Widget w;
7368      XEvent *event;
7369      String *prms;
7370      Cardinal *nprms;
7371 {
7372     Arg args[16];
7373
7374     appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
7375     ShowThinkingEvent();
7376
7377     if (appData.hideThinkingFromHuman) {
7378         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7379     } else {
7380         XtSetArg(args[0], XtNleftBitmap, None);
7381     }
7382     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
7383                 args, 1);
7384 }
7385
7386 void InfoProc(w, event, prms, nprms)
7387      Widget w;
7388      XEvent *event;
7389      String *prms;
7390      Cardinal *nprms;
7391 {
7392     char buf[MSG_SIZ];
7393     snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
7394             INFODIR, INFOFILE);
7395     system(buf);
7396 }
7397
7398 void ManProc(w, event, prms, nprms)
7399      Widget w;
7400      XEvent *event;
7401      String *prms;
7402      Cardinal *nprms;
7403 {
7404     char buf[MSG_SIZ];
7405     String name;
7406     if (nprms && *nprms > 0)
7407       name = prms[0];
7408     else
7409       name = "xboard";
7410     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
7411     system(buf);
7412 }
7413
7414 void HintProc(w, event, prms, nprms)
7415      Widget w;
7416      XEvent *event;
7417      String *prms;
7418      Cardinal *nprms;
7419 {
7420     HintEvent();
7421 }
7422
7423 void BookProc(w, event, prms, nprms)
7424      Widget w;
7425      XEvent *event;
7426      String *prms;
7427      Cardinal *nprms;
7428 {
7429     BookEvent();
7430 }
7431
7432 void AboutProc(w, event, prms, nprms)
7433      Widget w;
7434      XEvent *event;
7435      String *prms;
7436      Cardinal *nprms;
7437 {
7438     char buf[MSG_SIZ];
7439 #if ZIPPY
7440     char *zippy = " (with Zippy code)";
7441 #else
7442     char *zippy = "";
7443 #endif
7444     snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
7445             programVersion, zippy,
7446             "Copyright 1991 Digital Equipment Corporation",
7447             "Enhancements Copyright 1992-2009 Free Software Foundation",
7448             "Enhancements Copyright 2005 Alessandro Scotti",
7449             PACKAGE, " is free software and carries NO WARRANTY;",
7450             "see the file COPYING for more information.");
7451     ErrorPopUp(_("About XBoard"), buf, FALSE);
7452 }
7453
7454 void DebugProc(w, event, prms, nprms)
7455      Widget w;
7456      XEvent *event;
7457      String *prms;
7458      Cardinal *nprms;
7459 {
7460     appData.debugMode = !appData.debugMode;
7461 }
7462
7463 void AboutGameProc(w, event, prms, nprms)
7464      Widget w;
7465      XEvent *event;
7466      String *prms;
7467      Cardinal *nprms;
7468 {
7469     AboutGameEvent();
7470 }
7471
7472 void NothingProc(w, event, prms, nprms)
7473      Widget w;
7474      XEvent *event;
7475      String *prms;
7476      Cardinal *nprms;
7477 {
7478     return;
7479 }
7480
7481 void Iconify(w, event, prms, nprms)
7482      Widget w;
7483      XEvent *event;
7484      String *prms;
7485      Cardinal *nprms;
7486 {
7487     Arg args[16];
7488
7489     fromX = fromY = -1;
7490     XtSetArg(args[0], XtNiconic, True);
7491     XtSetValues(shellWidget, args, 1);
7492 }
7493
7494 void DisplayMessage(message, extMessage)
7495      char *message, *extMessage;
7496 {
7497   /* display a message in the message widget */
7498   
7499   char buf[MSG_SIZ];
7500   Arg arg;
7501   
7502   if (extMessage) 
7503     {
7504       if (*message) 
7505         {
7506           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
7507           message = buf;
7508         } 
7509       else 
7510         {
7511           message = extMessage;
7512         };
7513     };
7514   
7515   /* need to test if messageWidget already exists, since this function
7516      can also be called during the startup, if for example a Xresource
7517      is not set up correctly */
7518   if(messageWidget)
7519     {
7520       XtSetArg(arg, XtNlabel, message);
7521       XtSetValues(messageWidget, &arg, 1);
7522     };
7523   
7524   return;
7525 }
7526
7527 void DisplayTitle(text)
7528      char *text;
7529 {
7530     Arg args[16];
7531     int i;
7532     char title[MSG_SIZ];
7533     char icon[MSG_SIZ];
7534
7535     if (text == NULL) text = "";
7536
7537     if (appData.titleInWindow) {
7538         i = 0;
7539         XtSetArg(args[i], XtNlabel, text);   i++;
7540         XtSetValues(titleWidget, args, i);
7541     }
7542
7543     if (*text != NULLCHAR) {
7544         strcpy(icon, text);
7545         strcpy(title, text);
7546     } else if (appData.icsActive) {
7547         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
7548         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
7549     } else if (appData.cmailGameName[0] != NULLCHAR) {
7550         snprintf(icon, sizeof(icon), "%s", "CMail");
7551         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
7552 #ifdef GOTHIC
7553     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
7554     } else if (gameInfo.variant == VariantGothic) {
7555         strcpy(icon, programName);
7556         strcpy(title, GOTHIC);
7557 #endif
7558 #ifdef FALCON
7559     } else if (gameInfo.variant == VariantFalcon) {
7560         strcpy(icon, programName);
7561         strcpy(title, FALCON);
7562 #endif
7563     } else if (appData.noChessProgram) {
7564         strcpy(icon, programName);
7565         strcpy(title, programName);
7566     } else {
7567         strcpy(icon, first.tidy);
7568         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
7569     }
7570     i = 0;
7571     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
7572     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
7573     XtSetValues(shellWidget, args, i);
7574 }
7575
7576
7577 void DisplayError(message, error)
7578      String message;
7579      int error;
7580 {
7581     char buf[MSG_SIZ];
7582
7583     if (error == 0) {
7584         if (appData.debugMode || appData.matchMode) {
7585             fprintf(stderr, "%s: %s\n", programName, message);
7586         }
7587     } else {
7588         if (appData.debugMode || appData.matchMode) {
7589             fprintf(stderr, "%s: %s: %s\n",
7590                     programName, message, strerror(error));
7591         }
7592         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7593         message = buf;
7594     }
7595     ErrorPopUp(_("Error"), message, FALSE);
7596 }
7597
7598
7599 void DisplayMoveError(message)
7600      String message;
7601 {
7602     fromX = fromY = -1;
7603     ClearHighlights();
7604     DrawPosition(FALSE, NULL);
7605     if (appData.debugMode || appData.matchMode) {
7606         fprintf(stderr, "%s: %s\n", programName, message);
7607     }
7608     if (appData.popupMoveErrors) {
7609         ErrorPopUp(_("Error"), message, FALSE);
7610     } else {
7611         DisplayMessage(message, "");
7612     }
7613 }
7614
7615
7616 void DisplayFatalError(message, error, status)
7617      String message;
7618      int error, status;
7619 {
7620     char buf[MSG_SIZ];
7621
7622     errorExitStatus = status;
7623     if (error == 0) {
7624         fprintf(stderr, "%s: %s\n", programName, message);
7625     } else {
7626         fprintf(stderr, "%s: %s: %s\n",
7627                 programName, message, strerror(error));
7628         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7629         message = buf;
7630     }
7631     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7632       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7633     } else {
7634       ExitEvent(status);
7635     }
7636 }
7637
7638 void DisplayInformation(message)
7639      String message;
7640 {
7641     ErrorPopDown();
7642     ErrorPopUp(_("Information"), message, TRUE);
7643 }
7644
7645 void DisplayNote(message)
7646      String message;
7647 {
7648     ErrorPopDown();
7649     ErrorPopUp(_("Note"), message, FALSE);
7650 }
7651
7652 static int
7653 NullXErrorCheck(dpy, error_event)
7654      Display *dpy;
7655      XErrorEvent *error_event;
7656 {
7657     return 0;
7658 }
7659
7660 void DisplayIcsInteractionTitle(message)
7661      String message;
7662 {
7663   if (oldICSInteractionTitle == NULL) {
7664     /* Magic to find the old window title, adapted from vim */
7665     char *wina = getenv("WINDOWID");
7666     if (wina != NULL) {
7667       Window win = (Window) atoi(wina);
7668       Window root, parent, *children;
7669       unsigned int nchildren;
7670       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7671       for (;;) {
7672         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7673         if (!XQueryTree(xDisplay, win, &root, &parent,
7674                         &children, &nchildren)) break;
7675         if (children) XFree((void *)children);
7676         if (parent == root || parent == 0) break;
7677         win = parent;
7678       }
7679       XSetErrorHandler(oldHandler);
7680     }
7681     if (oldICSInteractionTitle == NULL) {
7682       oldICSInteractionTitle = "xterm";
7683     }
7684   }
7685   printf("\033]0;%s\007", message);
7686   fflush(stdout);
7687 }
7688
7689 char pendingReplyPrefix[MSG_SIZ];
7690 ProcRef pendingReplyPR;
7691
7692 void AskQuestionProc(w, event, prms, nprms)
7693      Widget w;
7694      XEvent *event;
7695      String *prms;
7696      Cardinal *nprms;
7697 {
7698     if (*nprms != 4) {
7699         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7700                 *nprms);
7701         return;
7702     }
7703     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7704 }
7705
7706 void AskQuestionPopDown()
7707 {
7708     if (!askQuestionUp) return;
7709     XtPopdown(askQuestionShell);
7710     XtDestroyWidget(askQuestionShell);
7711     askQuestionUp = False;
7712 }
7713
7714 void AskQuestionReplyAction(w, event, prms, nprms)
7715      Widget w;
7716      XEvent *event;
7717      String *prms;
7718      Cardinal *nprms;
7719 {
7720     char buf[MSG_SIZ];
7721     int err;
7722     String reply;
7723
7724     reply = XawDialogGetValueString(w = XtParent(w));
7725     strcpy(buf, pendingReplyPrefix);
7726     if (*buf) strcat(buf, " ");
7727     strcat(buf, reply);
7728     strcat(buf, "\n");
7729     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7730     AskQuestionPopDown();
7731
7732     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7733 }
7734
7735 void AskQuestionCallback(w, client_data, call_data)
7736      Widget w;
7737      XtPointer client_data, call_data;
7738 {
7739     String name;
7740     Arg args[16];
7741
7742     XtSetArg(args[0], XtNlabel, &name);
7743     XtGetValues(w, args, 1);
7744
7745     if (strcmp(name, _("cancel")) == 0) {
7746         AskQuestionPopDown();
7747     } else {
7748         AskQuestionReplyAction(w, NULL, NULL, NULL);
7749     }
7750 }
7751
7752 void AskQuestion(title, question, replyPrefix, pr)
7753      char *title, *question, *replyPrefix;
7754      ProcRef pr;
7755 {
7756     Arg args[16];
7757     Widget popup, layout, dialog, edit;
7758     Window root, child;
7759     int x, y, i;
7760     int win_x, win_y;
7761     unsigned int mask;
7762
7763     strcpy(pendingReplyPrefix, replyPrefix);
7764     pendingReplyPR = pr;
7765
7766     i = 0;
7767     XtSetArg(args[i], XtNresizable, True); i++;
7768     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7769     askQuestionShell = popup =
7770       XtCreatePopupShell(title, transientShellWidgetClass,
7771                          shellWidget, args, i);
7772
7773     layout =
7774       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7775                             layoutArgs, XtNumber(layoutArgs));
7776
7777     i = 0;
7778     XtSetArg(args[i], XtNlabel, question); i++;
7779     XtSetArg(args[i], XtNvalue, ""); i++;
7780     XtSetArg(args[i], XtNborderWidth, 0); i++;
7781     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7782                                    layout, args, i);
7783
7784     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7785                        (XtPointer) dialog);
7786     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7787                        (XtPointer) dialog);
7788
7789     XtRealizeWidget(popup);
7790     CatchDeleteWindow(popup, "AskQuestionPopDown");
7791
7792     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7793                   &x, &y, &win_x, &win_y, &mask);
7794
7795     XtSetArg(args[0], XtNx, x - 10);
7796     XtSetArg(args[1], XtNy, y - 30);
7797     XtSetValues(popup, args, 2);
7798
7799     XtPopup(popup, XtGrabExclusive);
7800     askQuestionUp = True;
7801
7802     edit = XtNameToWidget(dialog, "*value");
7803     XtSetKeyboardFocus(popup, edit);
7804 }
7805
7806
7807 void
7808 PlaySound(name)
7809      char *name;
7810 {
7811   if (*name == NULLCHAR) {
7812     return;
7813   } else if (strcmp(name, "$") == 0) {
7814     putc(BELLCHAR, stderr);
7815   } else {
7816     char buf[2048];
7817     snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
7818     system(buf);
7819   }
7820 }
7821
7822 void
7823 RingBell()
7824 {
7825   PlaySound(appData.soundMove);
7826 }
7827
7828 void
7829 PlayIcsWinSound()
7830 {
7831   PlaySound(appData.soundIcsWin);
7832 }
7833
7834 void
7835 PlayIcsLossSound()
7836 {
7837   PlaySound(appData.soundIcsLoss);
7838 }
7839
7840 void
7841 PlayIcsDrawSound()
7842 {
7843   PlaySound(appData.soundIcsDraw);
7844 }
7845
7846 void
7847 PlayIcsUnfinishedSound()
7848 {
7849   PlaySound(appData.soundIcsUnfinished);
7850 }
7851
7852 void
7853 PlayAlarmSound()
7854 {
7855   PlaySound(appData.soundIcsAlarm);
7856 }
7857
7858 void
7859 EchoOn()
7860 {
7861     system("stty echo");
7862 }
7863
7864 void
7865 EchoOff()
7866 {
7867     system("stty -echo");
7868 }
7869
7870 void
7871 Colorize(cc, continuation)
7872      ColorClass cc;
7873      int continuation;
7874 {
7875     char buf[MSG_SIZ];
7876     int count, outCount, error;
7877
7878     if (textColors[(int)cc].bg > 0) {
7879         if (textColors[(int)cc].fg > 0) {
7880             sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7881                     textColors[(int)cc].fg, textColors[(int)cc].bg);
7882         } else {
7883             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
7884                     textColors[(int)cc].bg);
7885         }
7886     } else {
7887         if (textColors[(int)cc].fg > 0) {
7888             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
7889                     textColors[(int)cc].fg);
7890         } else {
7891             sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
7892         }
7893     }
7894     count = strlen(buf);
7895     outCount = OutputToProcess(NoProc, buf, count, &error);
7896     if (outCount < count) {
7897         DisplayFatalError(_("Error writing to display"), error, 1);
7898     }
7899
7900     if (continuation) return;
7901     switch (cc) {
7902     case ColorShout:
7903       PlaySound(appData.soundShout);
7904       break;
7905     case ColorSShout:
7906       PlaySound(appData.soundSShout);
7907       break;
7908     case ColorChannel1:
7909       PlaySound(appData.soundChannel1);
7910       break;
7911     case ColorChannel:
7912       PlaySound(appData.soundChannel);
7913       break;
7914     case ColorKibitz:
7915       PlaySound(appData.soundKibitz);
7916       break;
7917     case ColorTell:
7918       PlaySound(appData.soundTell);
7919       break;
7920     case ColorChallenge:
7921       PlaySound(appData.soundChallenge);
7922       break;
7923     case ColorRequest:
7924       PlaySound(appData.soundRequest);
7925       break;
7926     case ColorSeek:
7927       PlaySound(appData.soundSeek);
7928       break;
7929     case ColorNormal:
7930     case ColorNone:
7931     default:
7932       break;
7933     }
7934 }
7935
7936 char *UserName()
7937 {
7938     return getpwuid(getuid())->pw_name;
7939 }
7940
7941 static char *ExpandPathName(path)
7942      char *path;
7943 {
7944     static char static_buf[2000];
7945     char *d, *s, buf[2000];
7946     struct passwd *pwd;
7947
7948     s = path;
7949     d = static_buf;
7950
7951     while (*s && isspace(*s))
7952       ++s;
7953
7954     if (!*s) {
7955         *d = 0;
7956         return static_buf;
7957     }
7958
7959     if (*s == '~') {
7960         if (*(s+1) == '/') {
7961             strcpy(d, getpwuid(getuid())->pw_dir);
7962             strcat(d, s+1);
7963         }
7964         else {
7965             strcpy(buf, s+1);
7966             *strchr(buf, '/') = 0;
7967             pwd = getpwnam(buf);
7968             if (!pwd)
7969               {
7970                   fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7971                           buf, path);
7972                   return NULL;
7973               }
7974             strcpy(d, pwd->pw_dir);
7975             strcat(d, strchr(s+1, '/'));
7976         }
7977     }
7978     else
7979       strcpy(d, s);
7980
7981     return static_buf;
7982 }
7983
7984 char *HostName()
7985 {
7986     static char host_name[MSG_SIZ];
7987
7988 #if HAVE_GETHOSTNAME
7989     gethostname(host_name, MSG_SIZ);
7990     return host_name;
7991 #else  /* not HAVE_GETHOSTNAME */
7992 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7993     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7994     return host_name;
7995 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7996     return "localhost";
7997 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7998 #endif /* not HAVE_GETHOSTNAME */
7999 }
8000
8001 XtIntervalId delayedEventTimerXID = 0;
8002 DelayedEventCallback delayedEventCallback = 0;
8003
8004 void
8005 FireDelayedEvent()
8006 {
8007     delayedEventTimerXID = 0;
8008     delayedEventCallback();
8009 }
8010
8011 void
8012 ScheduleDelayedEvent(cb, millisec)
8013      DelayedEventCallback cb; long millisec;
8014 {
8015     if(delayedEventTimerXID && delayedEventCallback == cb)
8016         // [HGM] alive: replace, rather than add or flush identical event
8017         XtRemoveTimeOut(delayedEventTimerXID);
8018     delayedEventCallback = cb;
8019     delayedEventTimerXID =
8020       XtAppAddTimeOut(appContext, millisec,
8021                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
8022 }
8023
8024 DelayedEventCallback
8025 GetDelayedEvent()
8026 {
8027   if (delayedEventTimerXID) {
8028     return delayedEventCallback;
8029   } else {
8030     return NULL;
8031   }
8032 }
8033
8034 void
8035 CancelDelayedEvent()
8036 {
8037   if (delayedEventTimerXID) {
8038     XtRemoveTimeOut(delayedEventTimerXID);
8039     delayedEventTimerXID = 0;
8040   }
8041 }
8042
8043 XtIntervalId loadGameTimerXID = 0;
8044
8045 int LoadGameTimerRunning()
8046 {
8047     return loadGameTimerXID != 0;
8048 }
8049
8050 int StopLoadGameTimer()
8051 {
8052     if (loadGameTimerXID != 0) {
8053         XtRemoveTimeOut(loadGameTimerXID);
8054         loadGameTimerXID = 0;
8055         return TRUE;
8056     } else {
8057         return FALSE;
8058     }
8059 }
8060
8061 void
8062 LoadGameTimerCallback(arg, id)
8063      XtPointer arg;
8064      XtIntervalId *id;
8065 {
8066     loadGameTimerXID = 0;
8067     AutoPlayGameLoop();
8068 }
8069
8070 void
8071 StartLoadGameTimer(millisec)
8072      long millisec;
8073 {
8074     loadGameTimerXID =
8075       XtAppAddTimeOut(appContext, millisec,
8076                       (XtTimerCallbackProc) LoadGameTimerCallback,
8077                       (XtPointer) 0);
8078 }
8079
8080 XtIntervalId analysisClockXID = 0;
8081
8082 void
8083 AnalysisClockCallback(arg, id)
8084      XtPointer arg;
8085      XtIntervalId *id;
8086 {
8087     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
8088          || appData.icsEngineAnalyze) { // [DM]
8089         AnalysisPeriodicEvent(0);
8090         StartAnalysisClock();
8091     }
8092 }
8093
8094 void
8095 StartAnalysisClock()
8096 {
8097     analysisClockXID =
8098       XtAppAddTimeOut(appContext, 2000,
8099                       (XtTimerCallbackProc) AnalysisClockCallback,
8100                       (XtPointer) 0);
8101 }
8102
8103 XtIntervalId clockTimerXID = 0;
8104
8105 int ClockTimerRunning()
8106 {
8107     return clockTimerXID != 0;
8108 }
8109
8110 int StopClockTimer()
8111 {
8112     if (clockTimerXID != 0) {
8113         XtRemoveTimeOut(clockTimerXID);
8114         clockTimerXID = 0;
8115         return TRUE;
8116     } else {
8117         return FALSE;
8118     }
8119 }
8120
8121 void
8122 ClockTimerCallback(arg, id)
8123      XtPointer arg;
8124      XtIntervalId *id;
8125 {
8126     clockTimerXID = 0;
8127     DecrementClocks();
8128 }
8129
8130 void
8131 StartClockTimer(millisec)
8132      long millisec;
8133 {
8134     clockTimerXID =
8135       XtAppAddTimeOut(appContext, millisec,
8136                       (XtTimerCallbackProc) ClockTimerCallback,
8137                       (XtPointer) 0);
8138 }
8139
8140 void
8141 DisplayTimerLabel(w, color, timer, highlight)
8142      Widget w;
8143      char *color;
8144      long timer;
8145      int highlight;
8146 {
8147     char buf[MSG_SIZ];
8148     Arg args[16];
8149
8150     /* check for low time warning */
8151     Pixel foregroundOrWarningColor = timerForegroundPixel;
8152
8153     if (timer > 0 &&
8154         appData.lowTimeWarning && 
8155         (timer / 1000) < appData.icsAlarmTime)
8156       foregroundOrWarningColor = lowTimeWarningColor;
8157
8158     if (appData.clockMode) {
8159         sprintf(buf, "%s: %s", color, TimeString(timer));
8160         XtSetArg(args[0], XtNlabel, buf);
8161     } else {
8162         sprintf(buf, "%s  ", color);
8163         XtSetArg(args[0], XtNlabel, buf);
8164     }
8165
8166     if (highlight) {
8167
8168         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
8169         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
8170     } else {
8171         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
8172         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
8173     }
8174
8175     XtSetValues(w, args, 3);
8176 }
8177
8178 void
8179 DisplayWhiteClock(timeRemaining, highlight)
8180      long timeRemaining;
8181      int highlight;
8182 {
8183     Arg args[16];
8184
8185     if(appData.noGUI) return;
8186     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
8187     if (highlight && iconPixmap == bIconPixmap) {
8188         iconPixmap = wIconPixmap;
8189         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
8190         XtSetValues(shellWidget, args, 1);
8191     }
8192 }
8193
8194 void
8195 DisplayBlackClock(timeRemaining, highlight)
8196      long timeRemaining;
8197      int highlight;
8198 {
8199     Arg args[16];
8200
8201     if(appData.noGUI) return;
8202     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
8203     if (highlight && iconPixmap == wIconPixmap) {
8204         iconPixmap = bIconPixmap;
8205         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
8206         XtSetValues(shellWidget, args, 1);
8207     }
8208 }
8209
8210 #define CPNone 0
8211 #define CPReal 1
8212 #define CPComm 2
8213 #define CPSock 3
8214 #define CPLoop 4
8215 typedef int CPKind;
8216
8217 typedef struct {
8218     CPKind kind;
8219     int pid;
8220     int fdTo, fdFrom;
8221 } ChildProc;
8222
8223
8224 int StartChildProcess(cmdLine, dir, pr)
8225      char *cmdLine;
8226      char *dir;
8227      ProcRef *pr;
8228 {
8229     char *argv[64], *p;
8230     int i, pid;
8231     int to_prog[2], from_prog[2];
8232     ChildProc *cp;
8233     char buf[MSG_SIZ];
8234
8235     if (appData.debugMode) {
8236         fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
8237     }
8238
8239     /* We do NOT feed the cmdLine to the shell; we just
8240        parse it into blank-separated arguments in the
8241        most simple-minded way possible.
8242        */
8243     i = 0;
8244     strcpy(buf, cmdLine);
8245     p = buf;
8246     for (;;) {
8247         argv[i++] = p;
8248         p = strchr(p, ' ');
8249         if (p == NULL) break;
8250         *p++ = NULLCHAR;
8251     }
8252     argv[i] = NULL;
8253
8254     SetUpChildIO(to_prog, from_prog);
8255
8256     if ((pid = fork()) == 0) {
8257         /* Child process */
8258         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
8259         close(to_prog[1]);     // first close the unused pipe ends
8260         close(from_prog[0]);
8261         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
8262         dup2(from_prog[1], 1);
8263         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
8264         close(from_prog[1]);                   // and closing again loses one of the pipes!
8265         if(fileno(stderr) >= 2) // better safe than sorry...
8266                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
8267
8268         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
8269             perror(dir);
8270             exit(1);
8271         }
8272
8273         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
8274
8275         execvp(argv[0], argv);
8276
8277         /* If we get here, exec failed */
8278         perror(argv[0]);
8279         exit(1);
8280     }
8281
8282     /* Parent process */
8283     close(to_prog[0]);
8284     close(from_prog[1]);
8285
8286     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8287     cp->kind = CPReal;
8288     cp->pid = pid;
8289     cp->fdFrom = from_prog[0];
8290     cp->fdTo = to_prog[1];
8291     *pr = (ProcRef) cp;
8292     return 0;
8293 }
8294
8295 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
8296 static RETSIGTYPE AlarmCallBack(int n)
8297 {
8298     return;
8299 }
8300
8301 void
8302 DestroyChildProcess(pr, signalType)
8303      ProcRef pr;
8304      int signalType;
8305 {
8306     ChildProc *cp = (ChildProc *) pr;
8307
8308     if (cp->kind != CPReal) return;
8309     cp->kind = CPNone;
8310     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
8311         signal(SIGALRM, AlarmCallBack);
8312         alarm(3);
8313         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
8314             kill(cp->pid, SIGKILL); // kill it forcefully
8315             wait((int *) 0);        // and wait again
8316         }
8317     } else {
8318         if (signalType) {
8319             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
8320         }
8321         /* Process is exiting either because of the kill or because of
8322            a quit command sent by the backend; either way, wait for it to die.
8323         */
8324         wait((int *) 0);
8325     }
8326     close(cp->fdFrom);
8327     close(cp->fdTo);
8328 }
8329
8330 void
8331 InterruptChildProcess(pr)
8332      ProcRef pr;
8333 {
8334     ChildProc *cp = (ChildProc *) pr;
8335
8336     if (cp->kind != CPReal) return;
8337     (void) kill(cp->pid, SIGINT); /* stop it thinking */
8338 }
8339
8340 int OpenTelnet(host, port, pr)
8341      char *host;
8342      char *port;
8343      ProcRef *pr;
8344 {
8345     char cmdLine[MSG_SIZ];
8346
8347     if (port[0] == NULLCHAR) {
8348       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
8349     } else {
8350       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
8351     }
8352     return StartChildProcess(cmdLine, "", pr);
8353 }
8354
8355 int OpenTCP(host, port, pr)
8356      char *host;
8357      char *port;
8358      ProcRef *pr;
8359 {
8360 #if OMIT_SOCKETS
8361     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
8362 #else  /* !OMIT_SOCKETS */
8363     int s;
8364     struct sockaddr_in sa;
8365     struct hostent     *hp;
8366     unsigned short uport;
8367     ChildProc *cp;
8368
8369     if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
8370         return errno;
8371     }
8372
8373     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
8374     sa.sin_family = AF_INET;
8375     sa.sin_addr.s_addr = INADDR_ANY;
8376     uport = (unsigned short) 0;
8377     sa.sin_port = htons(uport);
8378     if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
8379         return errno;
8380     }
8381
8382     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
8383     if (!(hp = gethostbyname(host))) {
8384         int b0, b1, b2, b3;
8385         if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
8386             hp = (struct hostent *) calloc(1, sizeof(struct hostent));
8387             hp->h_addrtype = AF_INET;
8388             hp->h_length = 4;
8389             hp->h_addr_list = (char **) calloc(2, sizeof(char *));
8390             hp->h_addr_list[0] = (char *) malloc(4);
8391             hp->h_addr_list[0][0] = b0;
8392             hp->h_addr_list[0][1] = b1;
8393             hp->h_addr_list[0][2] = b2;
8394             hp->h_addr_list[0][3] = b3;
8395         } else {
8396             return ENOENT;
8397         }
8398     }
8399     sa.sin_family = hp->h_addrtype;
8400     uport = (unsigned short) atoi(port);
8401     sa.sin_port = htons(uport);
8402     memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
8403
8404     if (connect(s, (struct sockaddr *) &sa,
8405                 sizeof(struct sockaddr_in)) < 0) {
8406         return errno;
8407     }
8408
8409     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8410     cp->kind = CPSock;
8411     cp->pid = 0;
8412     cp->fdFrom = s;
8413     cp->fdTo = s;
8414     *pr = (ProcRef) cp;
8415
8416 #endif /* !OMIT_SOCKETS */
8417
8418     return 0;
8419 }
8420
8421 int OpenCommPort(name, pr)
8422      char *name;
8423      ProcRef *pr;
8424 {
8425     int fd;
8426     ChildProc *cp;
8427
8428     fd = open(name, 2, 0);
8429     if (fd < 0) return errno;
8430
8431     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8432     cp->kind = CPComm;
8433     cp->pid = 0;
8434     cp->fdFrom = fd;
8435     cp->fdTo = fd;
8436     *pr = (ProcRef) cp;
8437
8438     return 0;
8439 }
8440
8441 int OpenLoopback(pr)
8442      ProcRef *pr;
8443 {
8444     ChildProc *cp;
8445     int to[2], from[2];
8446
8447     SetUpChildIO(to, from);
8448
8449     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8450     cp->kind = CPLoop;
8451     cp->pid = 0;
8452     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
8453     cp->fdTo = to[1];
8454     *pr = (ProcRef) cp;
8455
8456     return 0;
8457 }
8458
8459 int OpenRcmd(host, user, cmd, pr)
8460      char *host, *user, *cmd;
8461      ProcRef *pr;
8462 {
8463     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
8464     return -1;
8465 }
8466
8467 #define INPUT_SOURCE_BUF_SIZE 8192
8468
8469 typedef struct {
8470     CPKind kind;
8471     int fd;
8472     int lineByLine;
8473     char *unused;
8474     InputCallback func;
8475     XtInputId xid;
8476     char buf[INPUT_SOURCE_BUF_SIZE];
8477     VOIDSTAR closure;
8478 } InputSource;
8479
8480 void
8481 DoInputCallback(closure, source, xid)
8482      caddr_t closure;
8483      int *source;
8484      XtInputId *xid;
8485 {
8486     InputSource *is = (InputSource *) closure;
8487     int count;
8488     int error;
8489     char *p, *q;
8490
8491     if (is->lineByLine) {
8492         count = read(is->fd, is->unused,
8493                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
8494         if (count <= 0) {
8495             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
8496             return;
8497         }
8498         is->unused += count;
8499         p = is->buf;
8500         while (p < is->unused) {
8501             q = memchr(p, '\n', is->unused - p);
8502             if (q == NULL) break;
8503             q++;
8504             (is->func)(is, is->closure, p, q - p, 0);
8505             p = q;
8506         }
8507         q = is->buf;
8508         while (p < is->unused) {
8509             *q++ = *p++;
8510         }
8511         is->unused = q;
8512     } else {
8513         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
8514         if (count == -1)
8515           error = errno;
8516         else
8517           error = 0;
8518         (is->func)(is, is->closure, is->buf, count, error);
8519     }
8520 }
8521
8522 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
8523      ProcRef pr;
8524      int lineByLine;
8525      InputCallback func;
8526      VOIDSTAR closure;
8527 {
8528     InputSource *is;
8529     ChildProc *cp = (ChildProc *) pr;
8530
8531     is = (InputSource *) calloc(1, sizeof(InputSource));
8532     is->lineByLine = lineByLine;
8533     is->func = func;
8534     if (pr == NoProc) {
8535         is->kind = CPReal;
8536         is->fd = fileno(stdin);
8537     } else {
8538         is->kind = cp->kind;
8539         is->fd = cp->fdFrom;
8540     }
8541     if (lineByLine) {
8542         is->unused = is->buf;
8543     }
8544
8545     is->xid = XtAppAddInput(appContext, is->fd,
8546                             (XtPointer) (XtInputReadMask),
8547                             (XtInputCallbackProc) DoInputCallback,
8548                             (XtPointer) is);
8549     is->closure = closure;
8550     return (InputSourceRef) is;
8551 }
8552
8553 void
8554 RemoveInputSource(isr)
8555      InputSourceRef isr;
8556 {
8557     InputSource *is = (InputSource *) isr;
8558
8559     if (is->xid == 0) return;
8560     XtRemoveInput(is->xid);
8561     is->xid = 0;
8562 }
8563
8564 int OutputToProcess(pr, message, count, outError)
8565      ProcRef pr;
8566      char *message;
8567      int count;
8568      int *outError;
8569 {
8570     static int line = 0;
8571     ChildProc *cp = (ChildProc *) pr;
8572     int outCount;
8573
8574     if (pr == NoProc)
8575     {
8576         if (appData.noJoin || !appData.useInternalWrap)
8577             outCount = fwrite(message, 1, count, stdout);
8578         else
8579         {
8580             int width = get_term_width();
8581             int len = wrap(NULL, message, count, width, &line);
8582             char *msg = malloc(len);
8583             int dbgchk;
8584
8585             if (!msg)
8586                 outCount = fwrite(message, 1, count, stdout);
8587             else
8588             {
8589                 dbgchk = wrap(msg, message, count, width, &line);
8590                 if (dbgchk != len && appData.debugMode)
8591                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
8592                 outCount = fwrite(msg, 1, dbgchk, stdout);
8593                 free(msg);
8594             }
8595         }
8596     }
8597     else
8598       outCount = write(cp->fdTo, message, count);
8599
8600     if (outCount == -1)
8601       *outError = errno;
8602     else
8603       *outError = 0;
8604
8605     return outCount;
8606 }
8607
8608 /* Output message to process, with "ms" milliseconds of delay
8609    between each character. This is needed when sending the logon
8610    script to ICC, which for some reason doesn't like the
8611    instantaneous send. */
8612 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8613      ProcRef pr;
8614      char *message;
8615      int count;
8616      int *outError;
8617      long msdelay;
8618 {
8619     ChildProc *cp = (ChildProc *) pr;
8620     int outCount = 0;
8621     int r;
8622
8623     while (count--) {
8624         r = write(cp->fdTo, message++, 1);
8625         if (r == -1) {
8626             *outError = errno;
8627             return outCount;
8628         }
8629         ++outCount;
8630         if (msdelay >= 0)
8631           TimeDelay(msdelay);
8632     }
8633
8634     return outCount;
8635 }
8636
8637 /****   Animation code by Hugh Fisher, DCS, ANU.
8638
8639         Known problem: if a window overlapping the board is
8640         moved away while a piece is being animated underneath,
8641         the newly exposed area won't be updated properly.
8642         I can live with this.
8643
8644         Known problem: if you look carefully at the animation
8645         of pieces in mono mode, they are being drawn as solid
8646         shapes without interior detail while moving. Fixing
8647         this would be a major complication for minimal return.
8648 ****/
8649
8650 /*      Masks for XPM pieces. Black and white pieces can have
8651         different shapes, but in the interest of retaining my
8652         sanity pieces must have the same outline on both light
8653         and dark squares, and all pieces must use the same
8654         background square colors/images.                */
8655
8656 static int xpmDone = 0;
8657
8658 static void
8659 CreateAnimMasks (pieceDepth)
8660      int pieceDepth;
8661 {
8662   ChessSquare   piece;
8663   Pixmap        buf;
8664   GC            bufGC, maskGC;
8665   int           kind, n;
8666   unsigned long plane;
8667   XGCValues     values;
8668
8669   /* Need a bitmap just to get a GC with right depth */
8670   buf = XCreatePixmap(xDisplay, xBoardWindow,
8671                         8, 8, 1);
8672   values.foreground = 1;
8673   values.background = 0;
8674   /* Don't use XtGetGC, not read only */
8675   maskGC = XCreateGC(xDisplay, buf,
8676                     GCForeground | GCBackground, &values);
8677   XFreePixmap(xDisplay, buf);
8678
8679   buf = XCreatePixmap(xDisplay, xBoardWindow,
8680                       squareSize, squareSize, pieceDepth);
8681   values.foreground = XBlackPixel(xDisplay, xScreen);
8682   values.background = XWhitePixel(xDisplay, xScreen);
8683   bufGC = XCreateGC(xDisplay, buf,
8684                     GCForeground | GCBackground, &values);
8685
8686   for (piece = WhitePawn; piece <= BlackKing; piece++) {
8687     /* Begin with empty mask */
8688     if(!xpmDone) // [HGM] pieces: keep using existing
8689     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8690                                  squareSize, squareSize, 1);
8691     XSetFunction(xDisplay, maskGC, GXclear);
8692     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8693                    0, 0, squareSize, squareSize);
8694
8695     /* Take a copy of the piece */
8696     if (White(piece))
8697       kind = 0;
8698     else
8699       kind = 2;
8700     XSetFunction(xDisplay, bufGC, GXcopy);
8701     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8702               buf, bufGC,
8703               0, 0, squareSize, squareSize, 0, 0);
8704
8705     /* XOR the background (light) over the piece */
8706     XSetFunction(xDisplay, bufGC, GXxor);
8707     if (useImageSqs)
8708       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8709                 0, 0, squareSize, squareSize, 0, 0);
8710     else {
8711       XSetForeground(xDisplay, bufGC, lightSquareColor);
8712       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8713     }
8714
8715     /* We now have an inverted piece image with the background
8716        erased. Construct mask by just selecting all the non-zero
8717        pixels - no need to reconstruct the original image.      */
8718     XSetFunction(xDisplay, maskGC, GXor);
8719     plane = 1;
8720     /* Might be quicker to download an XImage and create bitmap
8721        data from it rather than this N copies per piece, but it
8722        only takes a fraction of a second and there is a much
8723        longer delay for loading the pieces.             */
8724     for (n = 0; n < pieceDepth; n ++) {
8725       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8726                  0, 0, squareSize, squareSize,
8727                  0, 0, plane);
8728       plane = plane << 1;
8729     }
8730   }
8731   /* Clean up */
8732   XFreePixmap(xDisplay, buf);
8733   XFreeGC(xDisplay, bufGC);
8734   XFreeGC(xDisplay, maskGC);
8735 }
8736
8737 static void
8738 InitAnimState (anim, info)
8739   AnimState * anim;
8740   XWindowAttributes * info;
8741 {
8742   XtGCMask  mask;
8743   XGCValues values;
8744
8745   /* Each buffer is square size, same depth as window */
8746   anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8747                         squareSize, squareSize, info->depth);
8748   anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8749                         squareSize, squareSize, info->depth);
8750
8751   /* Create a plain GC for blitting */
8752   mask = GCForeground | GCBackground | GCFunction |
8753          GCPlaneMask | GCGraphicsExposures;
8754   values.foreground = XBlackPixel(xDisplay, xScreen);
8755   values.background = XWhitePixel(xDisplay, xScreen);
8756   values.function   = GXcopy;
8757   values.plane_mask = AllPlanes;
8758   values.graphics_exposures = False;
8759   anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8760
8761   /* Piece will be copied from an existing context at
8762      the start of each new animation/drag. */
8763   anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8764
8765   /* Outline will be a read-only copy of an existing */
8766   anim->outlineGC = None;
8767 }
8768
8769 static void
8770 CreateAnimVars ()
8771 {
8772   static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
8773   XWindowAttributes info;
8774
8775   if (xpmDone && gameInfo.variant == old) return;
8776   if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
8777   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8778
8779   InitAnimState(&game, &info);
8780   InitAnimState(&player, &info);
8781
8782   /* For XPM pieces, we need bitmaps to use as masks. */
8783   if (useImages)
8784     CreateAnimMasks(info.depth);
8785    xpmDone = 1;
8786 }
8787
8788 #ifndef HAVE_USLEEP
8789
8790 static Boolean frameWaiting;
8791
8792 static RETSIGTYPE FrameAlarm (sig)
8793      int sig;
8794 {
8795   frameWaiting = False;
8796   /* In case System-V style signals.  Needed?? */
8797   signal(SIGALRM, FrameAlarm);
8798 }
8799
8800 static void
8801 FrameDelay (time)
8802      int time;
8803 {
8804   struct itimerval delay;
8805
8806   XSync(xDisplay, False);
8807
8808   if (time > 0) {
8809     frameWaiting = True;
8810     signal(SIGALRM, FrameAlarm);
8811     delay.it_interval.tv_sec =
8812       delay.it_value.tv_sec = time / 1000;
8813     delay.it_interval.tv_usec =
8814       delay.it_value.tv_usec = (time % 1000) * 1000;
8815     setitimer(ITIMER_REAL, &delay, NULL);
8816     while (frameWaiting) pause();
8817     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8818     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8819     setitimer(ITIMER_REAL, &delay, NULL);
8820   }
8821 }
8822
8823 #else
8824
8825 static void
8826 FrameDelay (time)
8827      int time;
8828 {
8829   XSync(xDisplay, False);
8830   if (time > 0)
8831     usleep(time * 1000);
8832 }
8833
8834 #endif
8835
8836 /*      Convert board position to corner of screen rect and color       */
8837
8838 static void
8839 ScreenSquare(column, row, pt, color)
8840      int column; int row; XPoint * pt; int * color;
8841 {
8842   if (flipView) {
8843     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8844     pt->y = lineGap + row * (squareSize + lineGap);
8845   } else {
8846     pt->x = lineGap + column * (squareSize + lineGap);
8847     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8848   }
8849   *color = SquareColor(row, column);
8850 }
8851
8852 /*      Convert window coords to square                 */
8853
8854 static void
8855 BoardSquare(x, y, column, row)
8856      int x; int y; int * column; int * row;
8857 {
8858   *column = EventToSquare(x, BOARD_WIDTH);
8859   if (flipView && *column >= 0)
8860     *column = BOARD_WIDTH - 1 - *column;
8861   *row = EventToSquare(y, BOARD_HEIGHT);
8862   if (!flipView && *row >= 0)
8863     *row = BOARD_HEIGHT - 1 - *row;
8864 }
8865
8866 /*   Utilities  */
8867
8868 #undef Max  /* just in case */
8869 #undef Min
8870 #define Max(a, b) ((a) > (b) ? (a) : (b))
8871 #define Min(a, b) ((a) < (b) ? (a) : (b))
8872
8873 static void
8874 SetRect(rect, x, y, width, height)
8875      XRectangle * rect; int x; int y; int width; int height;
8876 {
8877   rect->x = x;
8878   rect->y = y;
8879   rect->width  = width;
8880   rect->height = height;
8881 }
8882
8883 /*      Test if two frames overlap. If they do, return
8884         intersection rect within old and location of
8885         that rect within new. */
8886
8887 static Boolean
8888 Intersect(old, new, size, area, pt)
8889      XPoint * old; XPoint * new;
8890      int size; XRectangle * area; XPoint * pt;
8891 {
8892   if (old->x > new->x + size || new->x > old->x + size ||
8893       old->y > new->y + size || new->y > old->y + size) {
8894     return False;
8895   } else {
8896     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8897             size - abs(old->x - new->x), size - abs(old->y - new->y));
8898     pt->x = Max(old->x - new->x, 0);
8899     pt->y = Max(old->y - new->y, 0);
8900     return True;
8901   }
8902 }
8903
8904 /*      For two overlapping frames, return the rect(s)
8905         in the old that do not intersect with the new.   */
8906
8907 static void
8908 CalcUpdateRects(old, new, size, update, nUpdates)
8909      XPoint * old; XPoint * new; int size;
8910      XRectangle update[]; int * nUpdates;
8911 {
8912   int        count;
8913
8914   /* If old = new (shouldn't happen) then nothing to draw */
8915   if (old->x == new->x && old->y == new->y) {
8916     *nUpdates = 0;
8917     return;
8918   }
8919   /* Work out what bits overlap. Since we know the rects
8920      are the same size we don't need a full intersect calc. */
8921   count = 0;
8922   /* Top or bottom edge? */
8923   if (new->y > old->y) {
8924     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8925     count ++;
8926   } else if (old->y > new->y) {
8927     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8928                               size, old->y - new->y);
8929     count ++;
8930   }
8931   /* Left or right edge - don't overlap any update calculated above. */
8932   if (new->x > old->x) {
8933     SetRect(&(update[count]), old->x, Max(new->y, old->y),
8934                               new->x - old->x, size - abs(new->y - old->y));
8935     count ++;
8936   } else if (old->x > new->x) {
8937     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8938                               old->x - new->x, size - abs(new->y - old->y));
8939     count ++;
8940   }
8941   /* Done */
8942   *nUpdates = count;
8943 }
8944
8945 /*      Generate a series of frame coords from start->mid->finish.
8946         The movement rate doubles until the half way point is
8947         reached, then halves back down to the final destination,
8948         which gives a nice slow in/out effect. The algorithmn
8949         may seem to generate too many intermediates for short
8950         moves, but remember that the purpose is to attract the
8951         viewers attention to the piece about to be moved and
8952         then to where it ends up. Too few frames would be less
8953         noticeable.                                             */
8954
8955 static void
8956 Tween(start, mid, finish, factor, frames, nFrames)
8957      XPoint * start; XPoint * mid;
8958      XPoint * finish; int factor;
8959      XPoint frames[]; int * nFrames;
8960 {
8961   int fraction, n, count;
8962
8963   count = 0;
8964
8965   /* Slow in, stepping 1/16th, then 1/8th, ... */
8966   fraction = 1;
8967   for (n = 0; n < factor; n++)
8968     fraction *= 2;
8969   for (n = 0; n < factor; n++) {
8970     frames[count].x = start->x + (mid->x - start->x) / fraction;
8971     frames[count].y = start->y + (mid->y - start->y) / fraction;
8972     count ++;
8973     fraction = fraction / 2;
8974   }
8975
8976   /* Midpoint */
8977   frames[count] = *mid;
8978   count ++;
8979
8980   /* Slow out, stepping 1/2, then 1/4, ... */
8981   fraction = 2;
8982   for (n = 0; n < factor; n++) {
8983     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8984     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8985     count ++;
8986     fraction = fraction * 2;
8987   }
8988   *nFrames = count;
8989 }
8990
8991 /*      Draw a piece on the screen without disturbing what's there      */
8992
8993 static void
8994 SelectGCMask(piece, clip, outline, mask)
8995      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8996 {
8997   GC source;
8998
8999   /* Bitmap for piece being moved. */
9000   if (appData.monoMode) {
9001       *mask = *pieceToSolid(piece);
9002   } else if (useImages) {
9003 #if HAVE_LIBXPM
9004       *mask = xpmMask[piece];
9005 #else
9006       *mask = ximMaskPm[piece];
9007 #endif
9008   } else {
9009       *mask = *pieceToSolid(piece);
9010   }
9011
9012   /* GC for piece being moved. Square color doesn't matter, but
9013      since it gets modified we make a copy of the original. */
9014   if (White(piece)) {
9015     if (appData.monoMode)
9016       source = bwPieceGC;
9017     else
9018       source = wlPieceGC;
9019   } else {
9020     if (appData.monoMode)
9021       source = wbPieceGC;
9022     else
9023       source = blPieceGC;
9024   }
9025   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
9026
9027   /* Outline only used in mono mode and is not modified */
9028   if (White(piece))
9029     *outline = bwPieceGC;
9030   else
9031     *outline = wbPieceGC;
9032 }
9033
9034 static void
9035 OverlayPiece(piece, clip, outline,  dest)
9036      ChessSquare piece; GC clip; GC outline; Drawable dest;
9037 {
9038   int   kind;
9039
9040   if (!useImages) {
9041     /* Draw solid rectangle which will be clipped to shape of piece */
9042     XFillRectangle(xDisplay, dest, clip,
9043                    0, 0, squareSize, squareSize);
9044     if (appData.monoMode)
9045       /* Also draw outline in contrasting color for black
9046          on black / white on white cases                */
9047       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
9048                  0, 0, squareSize, squareSize, 0, 0, 1);
9049   } else {
9050     /* Copy the piece */
9051     if (White(piece))
9052       kind = 0;
9053     else
9054       kind = 2;
9055     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
9056               dest, clip,
9057               0, 0, squareSize, squareSize,
9058               0, 0);
9059   }
9060 }
9061
9062 /* Animate the movement of a single piece */
9063
9064 static void
9065 BeginAnimation(anim, piece, startColor, start)
9066      AnimState *anim;
9067      ChessSquare piece;
9068      int startColor;
9069      XPoint * start;
9070 {
9071   Pixmap mask;
9072
9073   /* The old buffer is initialised with the start square (empty) */
9074   BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
9075   anim->prevFrame = *start;
9076
9077   /* The piece will be drawn using its own bitmap as a matte    */
9078   SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
9079   XSetClipMask(xDisplay, anim->pieceGC, mask);
9080 }
9081
9082 static void
9083 AnimationFrame(anim, frame, piece)
9084      AnimState *anim;
9085      XPoint *frame;
9086      ChessSquare piece;
9087 {
9088   XRectangle updates[4];
9089   XRectangle overlap;
9090   XPoint     pt;
9091   int        count, i;
9092
9093   /* Save what we are about to draw into the new buffer */
9094   XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
9095             frame->x, frame->y, squareSize, squareSize,
9096             0, 0);
9097
9098   /* Erase bits of the previous frame */
9099   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
9100     /* Where the new frame overlapped the previous,
9101        the contents in newBuf are wrong. */
9102     XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
9103               overlap.x, overlap.y,
9104               overlap.width, overlap.height,
9105               pt.x, pt.y);
9106     /* Repaint the areas in the old that don't overlap new */
9107     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
9108     for (i = 0; i < count; i++)
9109       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
9110                 updates[i].x - anim->prevFrame.x,
9111                 updates[i].y - anim->prevFrame.y,
9112                 updates[i].width, updates[i].height,
9113                 updates[i].x, updates[i].y);
9114   } else {
9115     /* Easy when no overlap */
9116     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
9117                   0, 0, squareSize, squareSize,
9118                   anim->prevFrame.x, anim->prevFrame.y);
9119   }
9120
9121   /* Save this frame for next time round */
9122   XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
9123                 0, 0, squareSize, squareSize,
9124                 0, 0);
9125   anim->prevFrame = *frame;
9126
9127   /* Draw piece over original screen contents, not current,
9128      and copy entire rect. Wipes out overlapping piece images. */
9129   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
9130   XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
9131                 0, 0, squareSize, squareSize,
9132                 frame->x, frame->y);
9133 }
9134
9135 static void
9136 EndAnimation (anim, finish)
9137      AnimState *anim;
9138      XPoint *finish;
9139 {
9140   XRectangle updates[4];
9141   XRectangle overlap;
9142   XPoint     pt;
9143   int        count, i;
9144
9145   /* The main code will redraw the final square, so we
9146      only need to erase the bits that don't overlap.    */
9147   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
9148     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
9149     for (i = 0; i < count; i++)
9150       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
9151                 updates[i].x - anim->prevFrame.x,
9152                 updates[i].y - anim->prevFrame.y,
9153                 updates[i].width, updates[i].height,
9154                 updates[i].x, updates[i].y);
9155   } else {
9156     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
9157                 0, 0, squareSize, squareSize,
9158                 anim->prevFrame.x, anim->prevFrame.y);
9159   }
9160 }
9161
9162 static void
9163 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
9164      AnimState *anim;
9165      ChessSquare piece; int startColor;
9166      XPoint * start; XPoint * finish;
9167      XPoint frames[]; int nFrames;
9168 {
9169   int n;
9170
9171   BeginAnimation(anim, piece, startColor, start);
9172   for (n = 0; n < nFrames; n++) {
9173     AnimationFrame(anim, &(frames[n]), piece);
9174     FrameDelay(appData.animSpeed);
9175   }
9176   EndAnimation(anim, finish);
9177 }
9178
9179 /* Main control logic for deciding what to animate and how */
9180
9181 void
9182 AnimateMove(board, fromX, fromY, toX, toY)
9183      Board board;
9184      int fromX;
9185      int fromY;
9186      int toX;
9187      int toY;
9188 {
9189   ChessSquare piece;
9190   int hop;
9191   XPoint      start, finish, mid;
9192   XPoint      frames[kFactor * 2 + 1];
9193   int         nFrames, startColor, endColor;
9194
9195   /* Are we animating? */
9196   if (!appData.animate || appData.blindfold)
9197     return;
9198
9199   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing || 
9200      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing) 
9201         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
9202
9203   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
9204   piece = board[fromY][fromX];
9205   if (piece >= EmptySquare) return;
9206
9207 #if DONT_HOP
9208   hop = FALSE;
9209 #else
9210   hop = (piece == WhiteKnight || piece == BlackKnight);
9211 #endif
9212
9213   if (appData.debugMode) {
9214       fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
9215                              _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
9216              piece, fromX, fromY, toX, toY);  }
9217
9218   ScreenSquare(fromX, fromY, &start, &startColor);
9219   ScreenSquare(toX, toY, &finish, &endColor);
9220
9221   if (hop) {
9222     /* Knight: make diagonal movement then straight */
9223     if (abs(toY - fromY) < abs(toX - fromX)) {
9224        mid.x = start.x + (finish.x - start.x) / 2;
9225        mid.y = finish.y;
9226      } else {
9227        mid.x = finish.x;
9228        mid.y = start.y + (finish.y - start.y) / 2;
9229      }
9230   } else {
9231     mid.x = start.x + (finish.x - start.x) / 2;
9232     mid.y = start.y + (finish.y - start.y) / 2;
9233   }
9234
9235   /* Don't use as many frames for very short moves */
9236   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
9237     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
9238   else
9239     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
9240   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
9241
9242   /* Be sure end square is redrawn */
9243   damage[toY][toX] = True;
9244 }
9245
9246 void
9247 DragPieceBegin(x, y)
9248      int x; int y;
9249 {
9250     int  boardX, boardY, color;
9251     XPoint corner;
9252
9253     /* Are we animating? */
9254     if (!appData.animateDragging || appData.blindfold)
9255       return;
9256
9257     /* Figure out which square we start in and the
9258        mouse position relative to top left corner. */
9259     BoardSquare(x, y, &boardX, &boardY);
9260     player.startBoardX = boardX;
9261     player.startBoardY = boardY;
9262     ScreenSquare(boardX, boardY, &corner, &color);
9263     player.startSquare  = corner;
9264     player.startColor   = color;
9265     /* As soon as we start dragging, the piece will jump slightly to
9266        be centered over the mouse pointer. */
9267     player.mouseDelta.x = squareSize/2;
9268     player.mouseDelta.y = squareSize/2;
9269     /* Initialise animation */
9270     player.dragPiece = PieceForSquare(boardX, boardY);
9271     /* Sanity check */
9272     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
9273         player.dragActive = True;
9274         BeginAnimation(&player, player.dragPiece, color, &corner);
9275         /* Mark this square as needing to be redrawn. Note that
9276            we don't remove the piece though, since logically (ie
9277            as seen by opponent) the move hasn't been made yet. */
9278            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
9279               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
9280            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
9281                      corner.x, corner.y, squareSize, squareSize,
9282                      0, 0); // [HGM] zh: unstack in stead of grab
9283         damage[boardY][boardX] = True;
9284     } else {
9285         player.dragActive = False;
9286     }
9287 }
9288
9289 static void
9290 DragPieceMove(x, y)
9291      int x; int y;
9292 {
9293     XPoint corner;
9294
9295     /* Are we animating? */
9296     if (!appData.animateDragging || appData.blindfold)
9297       return;
9298
9299     /* Sanity check */
9300     if (! player.dragActive)
9301       return;
9302     /* Move piece, maintaining same relative position
9303        of mouse within square    */
9304     corner.x = x - player.mouseDelta.x;
9305     corner.y = y - player.mouseDelta.y;
9306     AnimationFrame(&player, &corner, player.dragPiece);
9307 #if HIGHDRAG
9308     if (appData.highlightDragging) {
9309         int boardX, boardY;
9310         BoardSquare(x, y, &boardX, &boardY);
9311         SetHighlights(fromX, fromY, boardX, boardY);
9312     }
9313 #endif
9314 }
9315
9316 void
9317 DragPieceEnd(x, y)
9318      int x; int y;
9319 {
9320     int boardX, boardY, color;
9321     XPoint corner;
9322
9323     /* Are we animating? */
9324     if (!appData.animateDragging || appData.blindfold)
9325       return;
9326
9327     /* Sanity check */
9328     if (! player.dragActive)
9329       return;
9330     /* Last frame in sequence is square piece is
9331        placed on, which may not match mouse exactly. */
9332     BoardSquare(x, y, &boardX, &boardY);
9333     ScreenSquare(boardX, boardY, &corner, &color);
9334     EndAnimation(&player, &corner);
9335
9336     /* Be sure end square is redrawn */
9337     damage[boardY][boardX] = True;
9338
9339     /* This prevents weird things happening with fast successive
9340        clicks which on my Sun at least can cause motion events
9341        without corresponding press/release. */
9342     player.dragActive = False;
9343 }
9344
9345 /* Handle expose event while piece being dragged */
9346
9347 static void
9348 DrawDragPiece ()
9349 {
9350   if (!player.dragActive || appData.blindfold)
9351     return;
9352
9353   /* What we're doing: logically, the move hasn't been made yet,
9354      so the piece is still in it's original square. But visually
9355      it's being dragged around the board. So we erase the square
9356      that the piece is on and draw it at the last known drag point. */
9357   BlankSquare(player.startSquare.x, player.startSquare.y,
9358                 player.startColor, EmptySquare, xBoardWindow);
9359   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
9360   damage[player.startBoardY][player.startBoardX] = TRUE;
9361 }
9362
9363 void
9364 SetProgramStats( FrontEndProgramStats * stats )
9365 {
9366   // [HR] TODO
9367   // [HGM] done, but perhaps backend should call this directly?
9368     EngineOutputUpdate( stats );
9369 }
9370
9371 #include <sys/ioctl.h>
9372 int get_term_width()
9373 {
9374     int fd, default_width;
9375
9376     fd = STDIN_FILENO;
9377     default_width = 79; // this is FICS default anyway...
9378
9379 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
9380     struct ttysize win;
9381     if (!ioctl(fd, TIOCGSIZE, &win))
9382         default_width = win.ts_cols;
9383 #elif defined(TIOCGWINSZ)
9384     struct winsize win;
9385     if (!ioctl(fd, TIOCGWINSZ, &win))
9386         default_width = win.ws_col;
9387 #endif
9388     return default_width;
9389 }
9390
9391 void update_ics_width()
9392 {
9393     static int old_width = 0;
9394     int new_width = get_term_width();
9395
9396     if (old_width != new_width)
9397        ics_printf("set width %d\n", new_width);
9398     old_width = new_width;
9399 }
9400
9401 void NotifyFrontendLogin()
9402 {
9403     update_ics_width();
9404 }