updated files for a new release, added a shortlog and a diffstat
[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 (selected_fen_position) free(selected_fen_position);
6309     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
6310     if (!selected_fen_position) return;
6311     XtOwnSelection(menuBarWidget, XA_PRIMARY,
6312                    CurrentTime,
6313                    SendPositionSelection,
6314                    NULL/* lose_ownership_proc */ ,
6315                    NULL/* transfer_done_proc */);
6316     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
6317                    CurrentTime,
6318                    SendPositionSelection,
6319                    NULL/* lose_ownership_proc */ ,
6320                    NULL/* transfer_done_proc */);
6321   }
6322
6323 /* function called when the data to Paste is ready */
6324 static void
6325 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
6326            Atom *type, XtPointer value, unsigned long *len, int *format)
6327 {
6328   char *fenstr=value;
6329   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
6330   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
6331   EditPositionPasteFEN(fenstr);
6332   XtFree(value);
6333 }
6334
6335 /* called when Paste Position button is pressed,
6336  * all parameters will be NULL */
6337 void PastePositionProc(w, event, prms, nprms)
6338   Widget w;
6339   XEvent *event;
6340   String *prms;
6341   Cardinal *nprms;
6342 {
6343     XtGetSelectionValue(menuBarWidget, 
6344       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
6345       /* (XtSelectionCallbackProc) */ PastePositionCB,
6346       NULL, /* client_data passed to PastePositionCB */
6347
6348       /* better to use the time field from the event that triggered the
6349        * call to this function, but that isn't trivial to get
6350        */
6351       CurrentTime
6352     );
6353     return;
6354 }
6355
6356 static Boolean
6357 SendGameSelection(Widget w, Atom *selection, Atom *target,
6358                   Atom *type_return, XtPointer *value_return,
6359                   unsigned long *length_return, int *format_return)
6360 {
6361   char *selection_tmp;
6362
6363   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
6364     FILE* f = fopen(gameCopyFilename, "r");
6365     long len;
6366     size_t count;
6367     if (f == NULL) return False;
6368     fseek(f, 0, 2);
6369     len = ftell(f);
6370     rewind(f);
6371     selection_tmp = XtMalloc(len + 1);
6372     count = fread(selection_tmp, 1, len, f);
6373     if (len != count) {
6374       XtFree(selection_tmp);
6375       return False;
6376     }
6377     selection_tmp[len] = NULLCHAR;
6378     *value_return = selection_tmp;
6379     *length_return = len;
6380     *type_return = *target;
6381     *format_return = 8; /* bits per byte */
6382     return True;
6383   } else if (*target == XA_TARGETS(xDisplay)) {
6384     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
6385     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
6386     targets_tmp[1] = XA_STRING;
6387     *value_return = targets_tmp;
6388     *type_return = XA_ATOM;
6389     *length_return = 2;
6390     *format_return = 8 * sizeof(Atom);
6391     if (*format_return > 32) {
6392       *length_return *= *format_return / 32;
6393       *format_return = 32;
6394     }
6395     return True;
6396   } else {
6397     return False;
6398   }
6399 }
6400
6401 /* note: when called from menu all parameters are NULL, so no clue what the
6402  * Widget which was clicked on was, or what the click event was
6403  */
6404 void CopyGameProc(w, event, prms, nprms)
6405   Widget w;
6406   XEvent *event;
6407   String *prms;
6408   Cardinal *nprms;
6409 {
6410   int ret;
6411
6412   ret = SaveGameToFile(gameCopyFilename, FALSE);
6413   if (!ret) return;
6414
6415   /*
6416    * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
6417    * have a notion of a game that is selected but not copied.
6418    * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
6419    */
6420   XtOwnSelection(menuBarWidget, XA_PRIMARY,
6421                  CurrentTime,
6422                  SendGameSelection,
6423                  NULL/* lose_ownership_proc */ ,
6424                  NULL/* transfer_done_proc */);
6425   XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
6426                  CurrentTime,
6427                  SendGameSelection,
6428                  NULL/* lose_ownership_proc */ ,
6429                  NULL/* transfer_done_proc */);
6430 }
6431
6432 /* function called when the data to Paste is ready */
6433 static void
6434 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
6435             Atom *type, XtPointer value, unsigned long *len, int *format)
6436 {
6437   FILE* f;
6438   if (value == NULL || *len == 0) {
6439     return; /* nothing had been selected to copy */
6440   }
6441   f = fopen(gamePasteFilename, "w");
6442   if (f == NULL) {
6443     DisplayError(_("Can't open temp file"), errno);
6444     return;
6445   }
6446   fwrite(value, 1, *len, f);
6447   fclose(f);
6448   XtFree(value);
6449   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
6450 }
6451
6452 /* called when Paste Game button is pressed,
6453  * all parameters will be NULL */
6454 void PasteGameProc(w, event, prms, nprms)
6455   Widget w;
6456   XEvent *event;
6457   String *prms;
6458   Cardinal *nprms;
6459 {
6460     XtGetSelectionValue(menuBarWidget,
6461       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
6462       /* (XtSelectionCallbackProc) */ PasteGameCB,
6463       NULL, /* client_data passed to PasteGameCB */
6464
6465       /* better to use the time field from the event that triggered the
6466        * call to this function, but that isn't trivial to get
6467        */
6468       CurrentTime
6469     );
6470     return;
6471 }
6472
6473
6474 void AutoSaveGame()
6475 {
6476     SaveGameProc(NULL, NULL, NULL, NULL);
6477 }
6478
6479
6480 void QuitProc(w, event, prms, nprms)
6481      Widget w;
6482      XEvent *event;
6483      String *prms;
6484      Cardinal *nprms;
6485 {
6486     ExitEvent(0);
6487 }
6488
6489 void PauseProc(w, event, prms, nprms)
6490      Widget w;
6491      XEvent *event;
6492      String *prms;
6493      Cardinal *nprms;
6494 {
6495     PauseEvent();
6496 }
6497
6498
6499 void MachineBlackProc(w, event, prms, nprms)
6500      Widget w;
6501      XEvent *event;
6502      String *prms;
6503      Cardinal *nprms;
6504 {
6505     MachineBlackEvent();
6506 }
6507
6508 void MachineWhiteProc(w, event, prms, nprms)
6509      Widget w;
6510      XEvent *event;
6511      String *prms;
6512      Cardinal *nprms;
6513 {
6514     MachineWhiteEvent();
6515 }
6516
6517 void AnalyzeModeProc(w, event, prms, nprms)
6518      Widget w;
6519      XEvent *event;
6520      String *prms;
6521      Cardinal *nprms;
6522 {
6523     char buf[MSG_SIZ];
6524
6525     if (!first.analysisSupport) {
6526       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6527       DisplayError(buf, 0);
6528       return;
6529     }
6530     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
6531     if (appData.icsActive) {
6532         if (gameMode != IcsObserving) {
6533             sprintf(buf,_("You are not observing a game"));
6534             DisplayError(buf, 0);
6535             /* secure check */
6536             if (appData.icsEngineAnalyze) {
6537                 if (appData.debugMode)
6538                     fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
6539                 ExitAnalyzeMode();
6540                 ModeHighlight();
6541             }
6542             return;
6543         }
6544         /* if enable, use want disable icsEngineAnalyze */
6545         if (appData.icsEngineAnalyze) {
6546                 ExitAnalyzeMode();
6547                 ModeHighlight();
6548                 return;
6549         }
6550         appData.icsEngineAnalyze = TRUE;
6551         if (appData.debugMode)
6552             fprintf(debugFP, _("ICS engine analyze starting... \n"));
6553     }
6554     if (!appData.showThinking)
6555       ShowThinkingProc(w,event,prms,nprms);
6556
6557     AnalyzeModeEvent();
6558 }
6559
6560 void AnalyzeFileProc(w, event, prms, nprms)
6561      Widget w;
6562      XEvent *event;
6563      String *prms;
6564      Cardinal *nprms;
6565 {
6566     if (!first.analysisSupport) {
6567       char buf[MSG_SIZ];
6568       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6569       DisplayError(buf, 0);
6570       return;
6571     }
6572     Reset(FALSE, TRUE);
6573
6574     if (!appData.showThinking)
6575       ShowThinkingProc(w,event,prms,nprms);
6576
6577     AnalyzeFileEvent();
6578     FileNamePopUp(_("File to analyze"), "", LoadGamePopUp, "rb");
6579     AnalysisPeriodicEvent(1);
6580 }
6581
6582 void TwoMachinesProc(w, event, prms, nprms)
6583      Widget w;
6584      XEvent *event;
6585      String *prms;
6586      Cardinal *nprms;
6587 {
6588     TwoMachinesEvent();
6589 }
6590
6591 void IcsClientProc(w, event, prms, nprms)
6592      Widget w;
6593      XEvent *event;
6594      String *prms;
6595      Cardinal *nprms;
6596 {
6597     IcsClientEvent();
6598 }
6599
6600 void EditGameProc(w, event, prms, nprms)
6601      Widget w;
6602      XEvent *event;
6603      String *prms;
6604      Cardinal *nprms;
6605 {
6606     EditGameEvent();
6607 }
6608
6609 void EditPositionProc(w, event, prms, nprms)
6610      Widget w;
6611      XEvent *event;
6612      String *prms;
6613      Cardinal *nprms;
6614 {
6615     EditPositionEvent();
6616 }
6617
6618 void TrainingProc(w, event, prms, nprms)
6619      Widget w;
6620      XEvent *event;
6621      String *prms;
6622      Cardinal *nprms;
6623 {
6624     TrainingEvent();
6625 }
6626
6627 void EditCommentProc(w, event, prms, nprms)
6628      Widget w;
6629      XEvent *event;
6630      String *prms;
6631      Cardinal *nprms;
6632 {
6633     if (editUp) {
6634         EditCommentPopDown();
6635     } else {
6636         EditCommentEvent();
6637     }
6638 }
6639
6640 void IcsInputBoxProc(w, event, prms, nprms)
6641      Widget w;
6642      XEvent *event;
6643      String *prms;
6644      Cardinal *nprms;
6645 {
6646     if (ICSInputBoxUp) {
6647         ICSInputBoxPopDown();
6648     } else {
6649         ICSInputBoxPopUp();
6650     }
6651 }
6652
6653 void AcceptProc(w, event, prms, nprms)
6654      Widget w;
6655      XEvent *event;
6656      String *prms;
6657      Cardinal *nprms;
6658 {
6659     AcceptEvent();
6660 }
6661
6662 void DeclineProc(w, event, prms, nprms)
6663      Widget w;
6664      XEvent *event;
6665      String *prms;
6666      Cardinal *nprms;
6667 {
6668     DeclineEvent();
6669 }
6670
6671 void RematchProc(w, event, prms, nprms)
6672      Widget w;
6673      XEvent *event;
6674      String *prms;
6675      Cardinal *nprms;
6676 {
6677     RematchEvent();
6678 }
6679
6680 void CallFlagProc(w, event, prms, nprms)
6681      Widget w;
6682      XEvent *event;
6683      String *prms;
6684      Cardinal *nprms;
6685 {
6686     CallFlagEvent();
6687 }
6688
6689 void DrawProc(w, event, prms, nprms)
6690      Widget w;
6691      XEvent *event;
6692      String *prms;
6693      Cardinal *nprms;
6694 {
6695     DrawEvent();
6696 }
6697
6698 void AbortProc(w, event, prms, nprms)
6699      Widget w;
6700      XEvent *event;
6701      String *prms;
6702      Cardinal *nprms;
6703 {
6704     AbortEvent();
6705 }
6706
6707 void AdjournProc(w, event, prms, nprms)
6708      Widget w;
6709      XEvent *event;
6710      String *prms;
6711      Cardinal *nprms;
6712 {
6713     AdjournEvent();
6714 }
6715
6716 void ResignProc(w, event, prms, nprms)
6717      Widget w;
6718      XEvent *event;
6719      String *prms;
6720      Cardinal *nprms;
6721 {
6722     ResignEvent();
6723 }
6724
6725 void AdjuWhiteProc(w, event, prms, nprms)
6726      Widget w;
6727      XEvent *event;
6728      String *prms;
6729      Cardinal *nprms;
6730 {
6731     UserAdjudicationEvent(+1);
6732 }
6733
6734 void AdjuBlackProc(w, event, prms, nprms)
6735      Widget w;
6736      XEvent *event;
6737      String *prms;
6738      Cardinal *nprms;
6739 {
6740     UserAdjudicationEvent(-1);
6741 }
6742
6743 void AdjuDrawProc(w, event, prms, nprms)
6744      Widget w;
6745      XEvent *event;
6746      String *prms;
6747      Cardinal *nprms;
6748 {
6749     UserAdjudicationEvent(0);
6750 }
6751
6752 void EnterKeyProc(w, event, prms, nprms)
6753      Widget w;
6754      XEvent *event;
6755      String *prms;
6756      Cardinal *nprms;
6757 {
6758     if (ICSInputBoxUp == True)
6759       ICSInputSendText();
6760 }
6761
6762 void StopObservingProc(w, event, prms, nprms)
6763      Widget w;
6764      XEvent *event;
6765      String *prms;
6766      Cardinal *nprms;
6767 {
6768     StopObservingEvent();
6769 }
6770
6771 void StopExaminingProc(w, event, prms, nprms)
6772      Widget w;
6773      XEvent *event;
6774      String *prms;
6775      Cardinal *nprms;
6776 {
6777     StopExaminingEvent();
6778 }
6779
6780
6781 void ForwardProc(w, event, prms, nprms)
6782      Widget w;
6783      XEvent *event;
6784      String *prms;
6785      Cardinal *nprms;
6786 {
6787     ForwardEvent();
6788 }
6789
6790
6791 void BackwardProc(w, event, prms, nprms)
6792      Widget w;
6793      XEvent *event;
6794      String *prms;
6795      Cardinal *nprms;
6796 {
6797     BackwardEvent();
6798 }
6799
6800 void ToStartProc(w, event, prms, nprms)
6801      Widget w;
6802      XEvent *event;
6803      String *prms;
6804      Cardinal *nprms;
6805 {
6806     ToStartEvent();
6807 }
6808
6809 void ToEndProc(w, event, prms, nprms)
6810      Widget w;
6811      XEvent *event;
6812      String *prms;
6813      Cardinal *nprms;
6814 {
6815     ToEndEvent();
6816 }
6817
6818 void RevertProc(w, event, prms, nprms)
6819      Widget w;
6820      XEvent *event;
6821      String *prms;
6822      Cardinal *nprms;
6823 {
6824     RevertEvent();
6825 }
6826
6827 void TruncateGameProc(w, event, prms, nprms)
6828      Widget w;
6829      XEvent *event;
6830      String *prms;
6831      Cardinal *nprms;
6832 {
6833     TruncateGameEvent();
6834 }
6835 void RetractMoveProc(w, event, prms, nprms)
6836      Widget w;
6837      XEvent *event;
6838      String *prms;
6839      Cardinal *nprms;
6840 {
6841     RetractMoveEvent();
6842 }
6843
6844 void MoveNowProc(w, event, prms, nprms)
6845      Widget w;
6846      XEvent *event;
6847      String *prms;
6848      Cardinal *nprms;
6849 {
6850     MoveNowEvent();
6851 }
6852
6853
6854 void AlwaysQueenProc(w, event, prms, nprms)
6855      Widget w;
6856      XEvent *event;
6857      String *prms;
6858      Cardinal *nprms;
6859 {
6860     Arg args[16];
6861
6862     appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6863
6864     if (appData.alwaysPromoteToQueen) {
6865         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6866     } else {
6867         XtSetArg(args[0], XtNleftBitmap, None);
6868     }
6869     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6870                 args, 1);
6871 }
6872
6873 void AnimateDraggingProc(w, event, prms, nprms)
6874      Widget w;
6875      XEvent *event;
6876      String *prms;
6877      Cardinal *nprms;
6878 {
6879     Arg args[16];
6880
6881     appData.animateDragging = !appData.animateDragging;
6882
6883     if (appData.animateDragging) {
6884         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6885         CreateAnimVars();
6886     } else {
6887         XtSetArg(args[0], XtNleftBitmap, None);
6888     }
6889     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6890                 args, 1);
6891 }
6892
6893 void AnimateMovingProc(w, event, prms, nprms)
6894      Widget w;
6895      XEvent *event;
6896      String *prms;
6897      Cardinal *nprms;
6898 {
6899     Arg args[16];
6900
6901     appData.animate = !appData.animate;
6902
6903     if (appData.animate) {
6904         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6905         CreateAnimVars();
6906     } else {
6907         XtSetArg(args[0], XtNleftBitmap, None);
6908     }
6909     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6910                 args, 1);
6911 }
6912
6913 void AutocommProc(w, event, prms, nprms)
6914      Widget w;
6915      XEvent *event;
6916      String *prms;
6917      Cardinal *nprms;
6918 {
6919     Arg args[16];
6920
6921     appData.autoComment = !appData.autoComment;
6922
6923     if (appData.autoComment) {
6924         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6925     } else {
6926         XtSetArg(args[0], XtNleftBitmap, None);
6927     }
6928     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
6929                 args, 1);
6930 }
6931
6932
6933 void AutoflagProc(w, event, prms, nprms)
6934      Widget w;
6935      XEvent *event;
6936      String *prms;
6937      Cardinal *nprms;
6938 {
6939     Arg args[16];
6940
6941     appData.autoCallFlag = !appData.autoCallFlag;
6942
6943     if (appData.autoCallFlag) {
6944         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6945     } else {
6946         XtSetArg(args[0], XtNleftBitmap, None);
6947     }
6948     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6949                 args, 1);
6950 }
6951
6952 void AutoflipProc(w, event, prms, nprms)
6953      Widget w;
6954      XEvent *event;
6955      String *prms;
6956      Cardinal *nprms;
6957 {
6958     Arg args[16];
6959
6960     appData.autoFlipView = !appData.autoFlipView;
6961
6962     if (appData.autoFlipView) {
6963         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6964     } else {
6965         XtSetArg(args[0], XtNleftBitmap, None);
6966     }
6967     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6968                 args, 1);
6969 }
6970
6971 void AutobsProc(w, event, prms, nprms)
6972      Widget w;
6973      XEvent *event;
6974      String *prms;
6975      Cardinal *nprms;
6976 {
6977     Arg args[16];
6978
6979     appData.autoObserve = !appData.autoObserve;
6980
6981     if (appData.autoObserve) {
6982         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6983     } else {
6984         XtSetArg(args[0], XtNleftBitmap, None);
6985     }
6986     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
6987                 args, 1);
6988 }
6989
6990 void AutoraiseProc(w, event, prms, nprms)
6991      Widget w;
6992      XEvent *event;
6993      String *prms;
6994      Cardinal *nprms;
6995 {
6996     Arg args[16];
6997
6998     appData.autoRaiseBoard = !appData.autoRaiseBoard;
6999
7000     if (appData.autoRaiseBoard) {
7001         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7002     } else {
7003         XtSetArg(args[0], XtNleftBitmap, None);
7004     }
7005     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Raise Board"),
7006                 args, 1);
7007 }
7008
7009 void AutosaveProc(w, event, prms, nprms)
7010      Widget w;
7011      XEvent *event;
7012      String *prms;
7013      Cardinal *nprms;
7014 {
7015     Arg args[16];
7016
7017     appData.autoSaveGames = !appData.autoSaveGames;
7018
7019     if (appData.autoSaveGames) {
7020         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7021     } else {
7022         XtSetArg(args[0], XtNleftBitmap, None);
7023     }
7024     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
7025                 args, 1);
7026 }
7027
7028 void BlindfoldProc(w, event, prms, nprms)
7029      Widget w;
7030      XEvent *event;
7031      String *prms;
7032      Cardinal *nprms;
7033 {
7034     Arg args[16];
7035
7036     appData.blindfold = !appData.blindfold;
7037
7038     if (appData.blindfold) {
7039         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7040     } else {
7041         XtSetArg(args[0], XtNleftBitmap, None);
7042     }
7043     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
7044                 args, 1);
7045
7046     DrawPosition(True, NULL);
7047 }
7048
7049 void TestLegalityProc(w, event, prms, nprms)
7050      Widget w;
7051      XEvent *event;
7052      String *prms;
7053      Cardinal *nprms;
7054 {
7055     Arg args[16];
7056
7057     appData.testLegality = !appData.testLegality;
7058
7059     if (appData.testLegality) {
7060         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7061     } else {
7062         XtSetArg(args[0], XtNleftBitmap, None);
7063     }
7064     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
7065                 args, 1);
7066 }
7067
7068
7069 void FlashMovesProc(w, event, prms, nprms)
7070      Widget w;
7071      XEvent *event;
7072      String *prms;
7073      Cardinal *nprms;
7074 {
7075     Arg args[16];
7076
7077     if (appData.flashCount == 0) {
7078         appData.flashCount = 3;
7079     } else {
7080         appData.flashCount = -appData.flashCount;
7081     }
7082
7083     if (appData.flashCount > 0) {
7084         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7085     } else {
7086         XtSetArg(args[0], XtNleftBitmap, None);
7087     }
7088     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
7089                 args, 1);
7090 }
7091
7092 void FlipViewProc(w, event, prms, nprms)
7093      Widget w;
7094      XEvent *event;
7095      String *prms;
7096      Cardinal *nprms;
7097 {
7098     flipView = !flipView;
7099     DrawPosition(True, NULL);
7100 }
7101
7102 void GetMoveListProc(w, event, prms, nprms)
7103      Widget w;
7104      XEvent *event;
7105      String *prms;
7106      Cardinal *nprms;
7107 {
7108     Arg args[16];
7109
7110     appData.getMoveList = !appData.getMoveList;
7111
7112     if (appData.getMoveList) {
7113         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7114         GetMoveListEvent();
7115     } else {
7116         XtSetArg(args[0], XtNleftBitmap, None);
7117     }
7118     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
7119                 args, 1);
7120 }
7121
7122 #if HIGHDRAG
7123 void HighlightDraggingProc(w, event, prms, nprms)
7124      Widget w;
7125      XEvent *event;
7126      String *prms;
7127      Cardinal *nprms;
7128 {
7129     Arg args[16];
7130
7131     appData.highlightDragging = !appData.highlightDragging;
7132
7133     if (appData.highlightDragging) {
7134         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7135     } else {
7136         XtSetArg(args[0], XtNleftBitmap, None);
7137     }
7138     XtSetValues(XtNameToWidget(menuBarWidget,
7139                                "menuOptions.Highlight Dragging"), args, 1);
7140 }
7141 #endif
7142
7143 void HighlightLastMoveProc(w, event, prms, nprms)
7144      Widget w;
7145      XEvent *event;
7146      String *prms;
7147      Cardinal *nprms;
7148 {
7149     Arg args[16];
7150
7151     appData.highlightLastMove = !appData.highlightLastMove;
7152
7153     if (appData.highlightLastMove) {
7154         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7155     } else {
7156         XtSetArg(args[0], XtNleftBitmap, None);
7157     }
7158     XtSetValues(XtNameToWidget(menuBarWidget,
7159                                "menuOptions.Highlight Last Move"), args, 1);
7160 }
7161
7162 void IcsAlarmProc(w, event, prms, nprms)
7163      Widget w;
7164      XEvent *event;
7165      String *prms;
7166      Cardinal *nprms;
7167 {
7168     Arg args[16];
7169
7170     appData.icsAlarm = !appData.icsAlarm;
7171
7172     if (appData.icsAlarm) {
7173         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7174     } else {
7175         XtSetArg(args[0], XtNleftBitmap, None);
7176     }
7177     XtSetValues(XtNameToWidget(menuBarWidget,
7178                                "menuOptions.ICS Alarm"), args, 1);
7179 }
7180
7181 void MoveSoundProc(w, event, prms, nprms)
7182      Widget w;
7183      XEvent *event;
7184      String *prms;
7185      Cardinal *nprms;
7186 {
7187     Arg args[16];
7188
7189     appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
7190
7191     if (appData.ringBellAfterMoves) {
7192         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7193     } else {
7194         XtSetArg(args[0], XtNleftBitmap, None);
7195     }
7196     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
7197                 args, 1);
7198 }
7199
7200
7201 void OldSaveStyleProc(w, event, prms, nprms)
7202      Widget w;
7203      XEvent *event;
7204      String *prms;
7205      Cardinal *nprms;
7206 {
7207     Arg args[16];
7208
7209     appData.oldSaveStyle = !appData.oldSaveStyle;
7210
7211     if (appData.oldSaveStyle) {
7212         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7213     } else {
7214         XtSetArg(args[0], XtNleftBitmap, None);
7215     }
7216     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Old Save Style"),
7217                 args, 1);
7218 }
7219
7220 void PeriodicUpdatesProc(w, event, prms, nprms)
7221      Widget w;
7222      XEvent *event;
7223      String *prms;
7224      Cardinal *nprms;
7225 {
7226     Arg args[16];
7227
7228     PeriodicUpdatesEvent(!appData.periodicUpdates);
7229
7230     if (appData.periodicUpdates) {
7231         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7232     } else {
7233         XtSetArg(args[0], XtNleftBitmap, None);
7234     }
7235     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
7236                 args, 1);
7237 }
7238
7239 void PonderNextMoveProc(w, event, prms, nprms)
7240      Widget w;
7241      XEvent *event;
7242      String *prms;
7243      Cardinal *nprms;
7244 {
7245     Arg args[16];
7246
7247     PonderNextMoveEvent(!appData.ponderNextMove);
7248
7249     if (appData.ponderNextMove) {
7250         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7251     } else {
7252         XtSetArg(args[0], XtNleftBitmap, None);
7253     }
7254     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
7255                 args, 1);
7256 }
7257
7258 void PopupExitMessageProc(w, event, prms, nprms)
7259      Widget w;
7260      XEvent *event;
7261      String *prms;
7262      Cardinal *nprms;
7263 {
7264     Arg args[16];
7265
7266     appData.popupExitMessage = !appData.popupExitMessage;
7267
7268     if (appData.popupExitMessage) {
7269         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7270     } else {
7271         XtSetArg(args[0], XtNleftBitmap, None);
7272     }
7273     XtSetValues(XtNameToWidget(menuBarWidget,
7274                                "menuOptions.Popup Exit Message"), args, 1);
7275 }
7276
7277 void PopupMoveErrorsProc(w, event, prms, nprms)
7278      Widget w;
7279      XEvent *event;
7280      String *prms;
7281      Cardinal *nprms;
7282 {
7283     Arg args[16];
7284
7285     appData.popupMoveErrors = !appData.popupMoveErrors;
7286
7287     if (appData.popupMoveErrors) {
7288         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7289     } else {
7290         XtSetArg(args[0], XtNleftBitmap, None);
7291     }
7292     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
7293                 args, 1);
7294 }
7295
7296 void PremoveProc(w, event, prms, nprms)
7297      Widget w;
7298      XEvent *event;
7299      String *prms;
7300      Cardinal *nprms;
7301 {
7302     Arg args[16];
7303
7304     appData.premove = !appData.premove;
7305
7306     if (appData.premove) {
7307         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7308     } else {
7309         XtSetArg(args[0], XtNleftBitmap, None);
7310     }
7311     XtSetValues(XtNameToWidget(menuBarWidget,
7312                                "menuOptions.Premove"), args, 1);
7313 }
7314
7315 void QuietPlayProc(w, event, prms, nprms)
7316      Widget w;
7317      XEvent *event;
7318      String *prms;
7319      Cardinal *nprms;
7320 {
7321     Arg args[16];
7322
7323     appData.quietPlay = !appData.quietPlay;
7324
7325     if (appData.quietPlay) {
7326         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7327     } else {
7328         XtSetArg(args[0], XtNleftBitmap, None);
7329     }
7330     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Quiet Play"),
7331                 args, 1);
7332 }
7333
7334 void ShowCoordsProc(w, event, prms, nprms)
7335      Widget w;
7336      XEvent *event;
7337      String *prms;
7338      Cardinal *nprms;
7339 {
7340     Arg args[16];
7341
7342     appData.showCoords = !appData.showCoords;
7343
7344     if (appData.showCoords) {
7345         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7346     } else {
7347         XtSetArg(args[0], XtNleftBitmap, None);
7348     }
7349     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
7350                 args, 1);
7351
7352     DrawPosition(True, NULL);
7353 }
7354
7355 void ShowThinkingProc(w, event, prms, nprms)
7356      Widget w;
7357      XEvent *event;
7358      String *prms;
7359      Cardinal *nprms;
7360 {
7361     appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
7362     ShowThinkingEvent();
7363 }
7364
7365 void HideThinkingProc(w, event, prms, nprms)
7366      Widget w;
7367      XEvent *event;
7368      String *prms;
7369      Cardinal *nprms;
7370 {
7371     Arg args[16];
7372
7373     appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
7374     ShowThinkingEvent();
7375
7376     if (appData.hideThinkingFromHuman) {
7377         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7378     } else {
7379         XtSetArg(args[0], XtNleftBitmap, None);
7380     }
7381     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
7382                 args, 1);
7383 }
7384
7385 void InfoProc(w, event, prms, nprms)
7386      Widget w;
7387      XEvent *event;
7388      String *prms;
7389      Cardinal *nprms;
7390 {
7391     char buf[MSG_SIZ];
7392     snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
7393             INFODIR, INFOFILE);
7394     system(buf);
7395 }
7396
7397 void ManProc(w, event, prms, nprms)
7398      Widget w;
7399      XEvent *event;
7400      String *prms;
7401      Cardinal *nprms;
7402 {
7403     char buf[MSG_SIZ];
7404     String name;
7405     if (nprms && *nprms > 0)
7406       name = prms[0];
7407     else
7408       name = "xboard";
7409     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
7410     system(buf);
7411 }
7412
7413 void HintProc(w, event, prms, nprms)
7414      Widget w;
7415      XEvent *event;
7416      String *prms;
7417      Cardinal *nprms;
7418 {
7419     HintEvent();
7420 }
7421
7422 void BookProc(w, event, prms, nprms)
7423      Widget w;
7424      XEvent *event;
7425      String *prms;
7426      Cardinal *nprms;
7427 {
7428     BookEvent();
7429 }
7430
7431 void AboutProc(w, event, prms, nprms)
7432      Widget w;
7433      XEvent *event;
7434      String *prms;
7435      Cardinal *nprms;
7436 {
7437     char buf[MSG_SIZ];
7438 #if ZIPPY
7439     char *zippy = " (with Zippy code)";
7440 #else
7441     char *zippy = "";
7442 #endif
7443     snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
7444             programVersion, zippy,
7445             "Copyright 1991 Digital Equipment Corporation",
7446             "Enhancements Copyright 1992-2009 Free Software Foundation",
7447             "Enhancements Copyright 2005 Alessandro Scotti",
7448             PACKAGE, " is free software and carries NO WARRANTY;",
7449             "see the file COPYING for more information.");
7450     ErrorPopUp(_("About XBoard"), buf, FALSE);
7451 }
7452
7453 void DebugProc(w, event, prms, nprms)
7454      Widget w;
7455      XEvent *event;
7456      String *prms;
7457      Cardinal *nprms;
7458 {
7459     appData.debugMode = !appData.debugMode;
7460 }
7461
7462 void AboutGameProc(w, event, prms, nprms)
7463      Widget w;
7464      XEvent *event;
7465      String *prms;
7466      Cardinal *nprms;
7467 {
7468     AboutGameEvent();
7469 }
7470
7471 void NothingProc(w, event, prms, nprms)
7472      Widget w;
7473      XEvent *event;
7474      String *prms;
7475      Cardinal *nprms;
7476 {
7477     return;
7478 }
7479
7480 void Iconify(w, event, prms, nprms)
7481      Widget w;
7482      XEvent *event;
7483      String *prms;
7484      Cardinal *nprms;
7485 {
7486     Arg args[16];
7487
7488     fromX = fromY = -1;
7489     XtSetArg(args[0], XtNiconic, True);
7490     XtSetValues(shellWidget, args, 1);
7491 }
7492
7493 void DisplayMessage(message, extMessage)
7494      char *message, *extMessage;
7495 {
7496   /* display a message in the message widget */
7497   
7498   char buf[MSG_SIZ];
7499   Arg arg;
7500   
7501   if (extMessage) 
7502     {
7503       if (*message) 
7504         {
7505           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
7506           message = buf;
7507         } 
7508       else 
7509         {
7510           message = extMessage;
7511         };
7512     };
7513   
7514   /* need to test if messageWidget already exists, since this function
7515      can also be called during the startup, if for example a Xresource
7516      is not set up correctly */
7517   if(messageWidget)
7518     {
7519       XtSetArg(arg, XtNlabel, message);
7520       XtSetValues(messageWidget, &arg, 1);
7521     };
7522   
7523   return;
7524 }
7525
7526 void DisplayTitle(text)
7527      char *text;
7528 {
7529     Arg args[16];
7530     int i;
7531     char title[MSG_SIZ];
7532     char icon[MSG_SIZ];
7533
7534     if (text == NULL) text = "";
7535
7536     if (appData.titleInWindow) {
7537         i = 0;
7538         XtSetArg(args[i], XtNlabel, text);   i++;
7539         XtSetValues(titleWidget, args, i);
7540     }
7541
7542     if (*text != NULLCHAR) {
7543         strcpy(icon, text);
7544         strcpy(title, text);
7545     } else if (appData.icsActive) {
7546         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
7547         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
7548     } else if (appData.cmailGameName[0] != NULLCHAR) {
7549         snprintf(icon, sizeof(icon), "%s", "CMail");
7550         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
7551 #ifdef GOTHIC
7552     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
7553     } else if (gameInfo.variant == VariantGothic) {
7554         strcpy(icon, programName);
7555         strcpy(title, GOTHIC);
7556 #endif
7557 #ifdef FALCON
7558     } else if (gameInfo.variant == VariantFalcon) {
7559         strcpy(icon, programName);
7560         strcpy(title, FALCON);
7561 #endif
7562     } else if (appData.noChessProgram) {
7563         strcpy(icon, programName);
7564         strcpy(title, programName);
7565     } else {
7566         strcpy(icon, first.tidy);
7567         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
7568     }
7569     i = 0;
7570     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
7571     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
7572     XtSetValues(shellWidget, args, i);
7573 }
7574
7575
7576 void DisplayError(message, error)
7577      String message;
7578      int error;
7579 {
7580     char buf[MSG_SIZ];
7581
7582     if (error == 0) {
7583         if (appData.debugMode || appData.matchMode) {
7584             fprintf(stderr, "%s: %s\n", programName, message);
7585         }
7586     } else {
7587         if (appData.debugMode || appData.matchMode) {
7588             fprintf(stderr, "%s: %s: %s\n",
7589                     programName, message, strerror(error));
7590         }
7591         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7592         message = buf;
7593     }
7594     ErrorPopUp(_("Error"), message, FALSE);
7595 }
7596
7597
7598 void DisplayMoveError(message)
7599      String message;
7600 {
7601     fromX = fromY = -1;
7602     ClearHighlights();
7603     DrawPosition(FALSE, NULL);
7604     if (appData.debugMode || appData.matchMode) {
7605         fprintf(stderr, "%s: %s\n", programName, message);
7606     }
7607     if (appData.popupMoveErrors) {
7608         ErrorPopUp(_("Error"), message, FALSE);
7609     } else {
7610         DisplayMessage(message, "");
7611     }
7612 }
7613
7614
7615 void DisplayFatalError(message, error, status)
7616      String message;
7617      int error, status;
7618 {
7619     char buf[MSG_SIZ];
7620
7621     errorExitStatus = status;
7622     if (error == 0) {
7623         fprintf(stderr, "%s: %s\n", programName, message);
7624     } else {
7625         fprintf(stderr, "%s: %s: %s\n",
7626                 programName, message, strerror(error));
7627         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7628         message = buf;
7629     }
7630     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7631       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7632     } else {
7633       ExitEvent(status);
7634     }
7635 }
7636
7637 void DisplayInformation(message)
7638      String message;
7639 {
7640     ErrorPopDown();
7641     ErrorPopUp(_("Information"), message, TRUE);
7642 }
7643
7644 void DisplayNote(message)
7645      String message;
7646 {
7647     ErrorPopDown();
7648     ErrorPopUp(_("Note"), message, FALSE);
7649 }
7650
7651 static int
7652 NullXErrorCheck(dpy, error_event)
7653      Display *dpy;
7654      XErrorEvent *error_event;
7655 {
7656     return 0;
7657 }
7658
7659 void DisplayIcsInteractionTitle(message)
7660      String message;
7661 {
7662   if (oldICSInteractionTitle == NULL) {
7663     /* Magic to find the old window title, adapted from vim */
7664     char *wina = getenv("WINDOWID");
7665     if (wina != NULL) {
7666       Window win = (Window) atoi(wina);
7667       Window root, parent, *children;
7668       unsigned int nchildren;
7669       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7670       for (;;) {
7671         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7672         if (!XQueryTree(xDisplay, win, &root, &parent,
7673                         &children, &nchildren)) break;
7674         if (children) XFree((void *)children);
7675         if (parent == root || parent == 0) break;
7676         win = parent;
7677       }
7678       XSetErrorHandler(oldHandler);
7679     }
7680     if (oldICSInteractionTitle == NULL) {
7681       oldICSInteractionTitle = "xterm";
7682     }
7683   }
7684   printf("\033]0;%s\007", message);
7685   fflush(stdout);
7686 }
7687
7688 char pendingReplyPrefix[MSG_SIZ];
7689 ProcRef pendingReplyPR;
7690
7691 void AskQuestionProc(w, event, prms, nprms)
7692      Widget w;
7693      XEvent *event;
7694      String *prms;
7695      Cardinal *nprms;
7696 {
7697     if (*nprms != 4) {
7698         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7699                 *nprms);
7700         return;
7701     }
7702     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7703 }
7704
7705 void AskQuestionPopDown()
7706 {
7707     if (!askQuestionUp) return;
7708     XtPopdown(askQuestionShell);
7709     XtDestroyWidget(askQuestionShell);
7710     askQuestionUp = False;
7711 }
7712
7713 void AskQuestionReplyAction(w, event, prms, nprms)
7714      Widget w;
7715      XEvent *event;
7716      String *prms;
7717      Cardinal *nprms;
7718 {
7719     char buf[MSG_SIZ];
7720     int err;
7721     String reply;
7722
7723     reply = XawDialogGetValueString(w = XtParent(w));
7724     strcpy(buf, pendingReplyPrefix);
7725     if (*buf) strcat(buf, " ");
7726     strcat(buf, reply);
7727     strcat(buf, "\n");
7728     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7729     AskQuestionPopDown();
7730
7731     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7732 }
7733
7734 void AskQuestionCallback(w, client_data, call_data)
7735      Widget w;
7736      XtPointer client_data, call_data;
7737 {
7738     String name;
7739     Arg args[16];
7740
7741     XtSetArg(args[0], XtNlabel, &name);
7742     XtGetValues(w, args, 1);
7743
7744     if (strcmp(name, _("cancel")) == 0) {
7745         AskQuestionPopDown();
7746     } else {
7747         AskQuestionReplyAction(w, NULL, NULL, NULL);
7748     }
7749 }
7750
7751 void AskQuestion(title, question, replyPrefix, pr)
7752      char *title, *question, *replyPrefix;
7753      ProcRef pr;
7754 {
7755     Arg args[16];
7756     Widget popup, layout, dialog, edit;
7757     Window root, child;
7758     int x, y, i;
7759     int win_x, win_y;
7760     unsigned int mask;
7761
7762     strcpy(pendingReplyPrefix, replyPrefix);
7763     pendingReplyPR = pr;
7764
7765     i = 0;
7766     XtSetArg(args[i], XtNresizable, True); i++;
7767     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7768     askQuestionShell = popup =
7769       XtCreatePopupShell(title, transientShellWidgetClass,
7770                          shellWidget, args, i);
7771
7772     layout =
7773       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7774                             layoutArgs, XtNumber(layoutArgs));
7775
7776     i = 0;
7777     XtSetArg(args[i], XtNlabel, question); i++;
7778     XtSetArg(args[i], XtNvalue, ""); i++;
7779     XtSetArg(args[i], XtNborderWidth, 0); i++;
7780     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7781                                    layout, args, i);
7782
7783     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7784                        (XtPointer) dialog);
7785     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7786                        (XtPointer) dialog);
7787
7788     XtRealizeWidget(popup);
7789     CatchDeleteWindow(popup, "AskQuestionPopDown");
7790
7791     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7792                   &x, &y, &win_x, &win_y, &mask);
7793
7794     XtSetArg(args[0], XtNx, x - 10);
7795     XtSetArg(args[1], XtNy, y - 30);
7796     XtSetValues(popup, args, 2);
7797
7798     XtPopup(popup, XtGrabExclusive);
7799     askQuestionUp = True;
7800
7801     edit = XtNameToWidget(dialog, "*value");
7802     XtSetKeyboardFocus(popup, edit);
7803 }
7804
7805
7806 void
7807 PlaySound(name)
7808      char *name;
7809 {
7810   if (*name == NULLCHAR) {
7811     return;
7812   } else if (strcmp(name, "$") == 0) {
7813     putc(BELLCHAR, stderr);
7814   } else {
7815     char buf[2048];
7816     snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
7817     system(buf);
7818   }
7819 }
7820
7821 void
7822 RingBell()
7823 {
7824   PlaySound(appData.soundMove);
7825 }
7826
7827 void
7828 PlayIcsWinSound()
7829 {
7830   PlaySound(appData.soundIcsWin);
7831 }
7832
7833 void
7834 PlayIcsLossSound()
7835 {
7836   PlaySound(appData.soundIcsLoss);
7837 }
7838
7839 void
7840 PlayIcsDrawSound()
7841 {
7842   PlaySound(appData.soundIcsDraw);
7843 }
7844
7845 void
7846 PlayIcsUnfinishedSound()
7847 {
7848   PlaySound(appData.soundIcsUnfinished);
7849 }
7850
7851 void
7852 PlayAlarmSound()
7853 {
7854   PlaySound(appData.soundIcsAlarm);
7855 }
7856
7857 void
7858 EchoOn()
7859 {
7860     system("stty echo");
7861 }
7862
7863 void
7864 EchoOff()
7865 {
7866     system("stty -echo");
7867 }
7868
7869 void
7870 Colorize(cc, continuation)
7871      ColorClass cc;
7872      int continuation;
7873 {
7874     char buf[MSG_SIZ];
7875     int count, outCount, error;
7876
7877     if (textColors[(int)cc].bg > 0) {
7878         if (textColors[(int)cc].fg > 0) {
7879             sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7880                     textColors[(int)cc].fg, textColors[(int)cc].bg);
7881         } else {
7882             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
7883                     textColors[(int)cc].bg);
7884         }
7885     } else {
7886         if (textColors[(int)cc].fg > 0) {
7887             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
7888                     textColors[(int)cc].fg);
7889         } else {
7890             sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
7891         }
7892     }
7893     count = strlen(buf);
7894     outCount = OutputToProcess(NoProc, buf, count, &error);
7895     if (outCount < count) {
7896         DisplayFatalError(_("Error writing to display"), error, 1);
7897     }
7898
7899     if (continuation) return;
7900     switch (cc) {
7901     case ColorShout:
7902       PlaySound(appData.soundShout);
7903       break;
7904     case ColorSShout:
7905       PlaySound(appData.soundSShout);
7906       break;
7907     case ColorChannel1:
7908       PlaySound(appData.soundChannel1);
7909       break;
7910     case ColorChannel:
7911       PlaySound(appData.soundChannel);
7912       break;
7913     case ColorKibitz:
7914       PlaySound(appData.soundKibitz);
7915       break;
7916     case ColorTell:
7917       PlaySound(appData.soundTell);
7918       break;
7919     case ColorChallenge:
7920       PlaySound(appData.soundChallenge);
7921       break;
7922     case ColorRequest:
7923       PlaySound(appData.soundRequest);
7924       break;
7925     case ColorSeek:
7926       PlaySound(appData.soundSeek);
7927       break;
7928     case ColorNormal:
7929     case ColorNone:
7930     default:
7931       break;
7932     }
7933 }
7934
7935 char *UserName()
7936 {
7937     return getpwuid(getuid())->pw_name;
7938 }
7939
7940 static char *ExpandPathName(path)
7941      char *path;
7942 {
7943     static char static_buf[2000];
7944     char *d, *s, buf[2000];
7945     struct passwd *pwd;
7946
7947     s = path;
7948     d = static_buf;
7949
7950     while (*s && isspace(*s))
7951       ++s;
7952
7953     if (!*s) {
7954         *d = 0;
7955         return static_buf;
7956     }
7957
7958     if (*s == '~') {
7959         if (*(s+1) == '/') {
7960             strcpy(d, getpwuid(getuid())->pw_dir);
7961             strcat(d, s+1);
7962         }
7963         else {
7964             strcpy(buf, s+1);
7965             *strchr(buf, '/') = 0;
7966             pwd = getpwnam(buf);
7967             if (!pwd)
7968               {
7969                   fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7970                           buf, path);
7971                   return NULL;
7972               }
7973             strcpy(d, pwd->pw_dir);
7974             strcat(d, strchr(s+1, '/'));
7975         }
7976     }
7977     else
7978       strcpy(d, s);
7979
7980     return static_buf;
7981 }
7982
7983 char *HostName()
7984 {
7985     static char host_name[MSG_SIZ];
7986
7987 #if HAVE_GETHOSTNAME
7988     gethostname(host_name, MSG_SIZ);
7989     return host_name;
7990 #else  /* not HAVE_GETHOSTNAME */
7991 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7992     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7993     return host_name;
7994 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7995     return "localhost";
7996 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7997 #endif /* not HAVE_GETHOSTNAME */
7998 }
7999
8000 XtIntervalId delayedEventTimerXID = 0;
8001 DelayedEventCallback delayedEventCallback = 0;
8002
8003 void
8004 FireDelayedEvent()
8005 {
8006     delayedEventTimerXID = 0;
8007     delayedEventCallback();
8008 }
8009
8010 void
8011 ScheduleDelayedEvent(cb, millisec)
8012      DelayedEventCallback cb; long millisec;
8013 {
8014     if(delayedEventTimerXID && delayedEventCallback == cb)
8015         // [HGM] alive: replace, rather than add or flush identical event
8016         XtRemoveTimeOut(delayedEventTimerXID);
8017     delayedEventCallback = cb;
8018     delayedEventTimerXID =
8019       XtAppAddTimeOut(appContext, millisec,
8020                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
8021 }
8022
8023 DelayedEventCallback
8024 GetDelayedEvent()
8025 {
8026   if (delayedEventTimerXID) {
8027     return delayedEventCallback;
8028   } else {
8029     return NULL;
8030   }
8031 }
8032
8033 void
8034 CancelDelayedEvent()
8035 {
8036   if (delayedEventTimerXID) {
8037     XtRemoveTimeOut(delayedEventTimerXID);
8038     delayedEventTimerXID = 0;
8039   }
8040 }
8041
8042 XtIntervalId loadGameTimerXID = 0;
8043
8044 int LoadGameTimerRunning()
8045 {
8046     return loadGameTimerXID != 0;
8047 }
8048
8049 int StopLoadGameTimer()
8050 {
8051     if (loadGameTimerXID != 0) {
8052         XtRemoveTimeOut(loadGameTimerXID);
8053         loadGameTimerXID = 0;
8054         return TRUE;
8055     } else {
8056         return FALSE;
8057     }
8058 }
8059
8060 void
8061 LoadGameTimerCallback(arg, id)
8062      XtPointer arg;
8063      XtIntervalId *id;
8064 {
8065     loadGameTimerXID = 0;
8066     AutoPlayGameLoop();
8067 }
8068
8069 void
8070 StartLoadGameTimer(millisec)
8071      long millisec;
8072 {
8073     loadGameTimerXID =
8074       XtAppAddTimeOut(appContext, millisec,
8075                       (XtTimerCallbackProc) LoadGameTimerCallback,
8076                       (XtPointer) 0);
8077 }
8078
8079 XtIntervalId analysisClockXID = 0;
8080
8081 void
8082 AnalysisClockCallback(arg, id)
8083      XtPointer arg;
8084      XtIntervalId *id;
8085 {
8086     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
8087          || appData.icsEngineAnalyze) { // [DM]
8088         AnalysisPeriodicEvent(0);
8089         StartAnalysisClock();
8090     }
8091 }
8092
8093 void
8094 StartAnalysisClock()
8095 {
8096     analysisClockXID =
8097       XtAppAddTimeOut(appContext, 2000,
8098                       (XtTimerCallbackProc) AnalysisClockCallback,
8099                       (XtPointer) 0);
8100 }
8101
8102 XtIntervalId clockTimerXID = 0;
8103
8104 int ClockTimerRunning()
8105 {
8106     return clockTimerXID != 0;
8107 }
8108
8109 int StopClockTimer()
8110 {
8111     if (clockTimerXID != 0) {
8112         XtRemoveTimeOut(clockTimerXID);
8113         clockTimerXID = 0;
8114         return TRUE;
8115     } else {
8116         return FALSE;
8117     }
8118 }
8119
8120 void
8121 ClockTimerCallback(arg, id)
8122      XtPointer arg;
8123      XtIntervalId *id;
8124 {
8125     clockTimerXID = 0;
8126     DecrementClocks();
8127 }
8128
8129 void
8130 StartClockTimer(millisec)
8131      long millisec;
8132 {
8133     clockTimerXID =
8134       XtAppAddTimeOut(appContext, millisec,
8135                       (XtTimerCallbackProc) ClockTimerCallback,
8136                       (XtPointer) 0);
8137 }
8138
8139 void
8140 DisplayTimerLabel(w, color, timer, highlight)
8141      Widget w;
8142      char *color;
8143      long timer;
8144      int highlight;
8145 {
8146     char buf[MSG_SIZ];
8147     Arg args[16];
8148
8149     /* check for low time warning */
8150     Pixel foregroundOrWarningColor = timerForegroundPixel;
8151
8152     if (timer > 0 &&
8153         appData.lowTimeWarning && 
8154         (timer / 1000) < appData.icsAlarmTime)
8155       foregroundOrWarningColor = lowTimeWarningColor;
8156
8157     if (appData.clockMode) {
8158         sprintf(buf, "%s: %s", color, TimeString(timer));
8159         XtSetArg(args[0], XtNlabel, buf);
8160     } else {
8161         sprintf(buf, "%s  ", color);
8162         XtSetArg(args[0], XtNlabel, buf);
8163     }
8164
8165     if (highlight) {
8166
8167         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
8168         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
8169     } else {
8170         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
8171         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
8172     }
8173
8174     XtSetValues(w, args, 3);
8175 }
8176
8177 void
8178 DisplayWhiteClock(timeRemaining, highlight)
8179      long timeRemaining;
8180      int highlight;
8181 {
8182     Arg args[16];
8183
8184     if(appData.noGUI) return;
8185     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
8186     if (highlight && iconPixmap == bIconPixmap) {
8187         iconPixmap = wIconPixmap;
8188         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
8189         XtSetValues(shellWidget, args, 1);
8190     }
8191 }
8192
8193 void
8194 DisplayBlackClock(timeRemaining, highlight)
8195      long timeRemaining;
8196      int highlight;
8197 {
8198     Arg args[16];
8199
8200     if(appData.noGUI) return;
8201     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
8202     if (highlight && iconPixmap == wIconPixmap) {
8203         iconPixmap = bIconPixmap;
8204         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
8205         XtSetValues(shellWidget, args, 1);
8206     }
8207 }
8208
8209 #define CPNone 0
8210 #define CPReal 1
8211 #define CPComm 2
8212 #define CPSock 3
8213 #define CPLoop 4
8214 typedef int CPKind;
8215
8216 typedef struct {
8217     CPKind kind;
8218     int pid;
8219     int fdTo, fdFrom;
8220 } ChildProc;
8221
8222
8223 int StartChildProcess(cmdLine, dir, pr)
8224      char *cmdLine;
8225      char *dir;
8226      ProcRef *pr;
8227 {
8228     char *argv[64], *p;
8229     int i, pid;
8230     int to_prog[2], from_prog[2];
8231     ChildProc *cp;
8232     char buf[MSG_SIZ];
8233
8234     if (appData.debugMode) {
8235         fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
8236     }
8237
8238     /* We do NOT feed the cmdLine to the shell; we just
8239        parse it into blank-separated arguments in the
8240        most simple-minded way possible.
8241        */
8242     i = 0;
8243     strcpy(buf, cmdLine);
8244     p = buf;
8245     for (;;) {
8246         argv[i++] = p;
8247         p = strchr(p, ' ');
8248         if (p == NULL) break;
8249         *p++ = NULLCHAR;
8250     }
8251     argv[i] = NULL;
8252
8253     SetUpChildIO(to_prog, from_prog);
8254
8255     if ((pid = fork()) == 0) {
8256         /* Child process */
8257         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
8258         close(to_prog[1]);     // first close the unused pipe ends
8259         close(from_prog[0]);
8260         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
8261         dup2(from_prog[1], 1);
8262         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
8263         close(from_prog[1]);                   // and closing again loses one of the pipes!
8264         if(fileno(stderr) >= 2) // better safe than sorry...
8265                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
8266
8267         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
8268             perror(dir);
8269             exit(1);
8270         }
8271
8272         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
8273
8274         execvp(argv[0], argv);
8275
8276         /* If we get here, exec failed */
8277         perror(argv[0]);
8278         exit(1);
8279     }
8280
8281     /* Parent process */
8282     close(to_prog[0]);
8283     close(from_prog[1]);
8284
8285     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8286     cp->kind = CPReal;
8287     cp->pid = pid;
8288     cp->fdFrom = from_prog[0];
8289     cp->fdTo = to_prog[1];
8290     *pr = (ProcRef) cp;
8291     return 0;
8292 }
8293
8294 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
8295 static RETSIGTYPE AlarmCallBack(int n)
8296 {
8297     return;
8298 }
8299
8300 void
8301 DestroyChildProcess(pr, signalType)
8302      ProcRef pr;
8303      int signalType;
8304 {
8305     ChildProc *cp = (ChildProc *) pr;
8306
8307     if (cp->kind != CPReal) return;
8308     cp->kind = CPNone;
8309     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
8310         signal(SIGALRM, AlarmCallBack);
8311         alarm(3);
8312         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
8313             kill(cp->pid, SIGKILL); // kill it forcefully
8314             wait((int *) 0);        // and wait again
8315         }
8316     } else {
8317         if (signalType) {
8318             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
8319         }
8320         /* Process is exiting either because of the kill or because of
8321            a quit command sent by the backend; either way, wait for it to die.
8322         */
8323         wait((int *) 0);
8324     }
8325     close(cp->fdFrom);
8326     close(cp->fdTo);
8327 }
8328
8329 void
8330 InterruptChildProcess(pr)
8331      ProcRef pr;
8332 {
8333     ChildProc *cp = (ChildProc *) pr;
8334
8335     if (cp->kind != CPReal) return;
8336     (void) kill(cp->pid, SIGINT); /* stop it thinking */
8337 }
8338
8339 int OpenTelnet(host, port, pr)
8340      char *host;
8341      char *port;
8342      ProcRef *pr;
8343 {
8344     char cmdLine[MSG_SIZ];
8345
8346     if (port[0] == NULLCHAR) {
8347       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
8348     } else {
8349       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
8350     }
8351     return StartChildProcess(cmdLine, "", pr);
8352 }
8353
8354 int OpenTCP(host, port, pr)
8355      char *host;
8356      char *port;
8357      ProcRef *pr;
8358 {
8359 #if OMIT_SOCKETS
8360     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
8361 #else  /* !OMIT_SOCKETS */
8362     int s;
8363     struct sockaddr_in sa;
8364     struct hostent     *hp;
8365     unsigned short uport;
8366     ChildProc *cp;
8367
8368     if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
8369         return errno;
8370     }
8371
8372     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
8373     sa.sin_family = AF_INET;
8374     sa.sin_addr.s_addr = INADDR_ANY;
8375     uport = (unsigned short) 0;
8376     sa.sin_port = htons(uport);
8377     if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
8378         return errno;
8379     }
8380
8381     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
8382     if (!(hp = gethostbyname(host))) {
8383         int b0, b1, b2, b3;
8384         if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
8385             hp = (struct hostent *) calloc(1, sizeof(struct hostent));
8386             hp->h_addrtype = AF_INET;
8387             hp->h_length = 4;
8388             hp->h_addr_list = (char **) calloc(2, sizeof(char *));
8389             hp->h_addr_list[0] = (char *) malloc(4);
8390             hp->h_addr_list[0][0] = b0;
8391             hp->h_addr_list[0][1] = b1;
8392             hp->h_addr_list[0][2] = b2;
8393             hp->h_addr_list[0][3] = b3;
8394         } else {
8395             return ENOENT;
8396         }
8397     }
8398     sa.sin_family = hp->h_addrtype;
8399     uport = (unsigned short) atoi(port);
8400     sa.sin_port = htons(uport);
8401     memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
8402
8403     if (connect(s, (struct sockaddr *) &sa,
8404                 sizeof(struct sockaddr_in)) < 0) {
8405         return errno;
8406     }
8407
8408     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8409     cp->kind = CPSock;
8410     cp->pid = 0;
8411     cp->fdFrom = s;
8412     cp->fdTo = s;
8413     *pr = (ProcRef) cp;
8414
8415 #endif /* !OMIT_SOCKETS */
8416
8417     return 0;
8418 }
8419
8420 int OpenCommPort(name, pr)
8421      char *name;
8422      ProcRef *pr;
8423 {
8424     int fd;
8425     ChildProc *cp;
8426
8427     fd = open(name, 2, 0);
8428     if (fd < 0) return errno;
8429
8430     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8431     cp->kind = CPComm;
8432     cp->pid = 0;
8433     cp->fdFrom = fd;
8434     cp->fdTo = fd;
8435     *pr = (ProcRef) cp;
8436
8437     return 0;
8438 }
8439
8440 int OpenLoopback(pr)
8441      ProcRef *pr;
8442 {
8443     ChildProc *cp;
8444     int to[2], from[2];
8445
8446     SetUpChildIO(to, from);
8447
8448     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8449     cp->kind = CPLoop;
8450     cp->pid = 0;
8451     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
8452     cp->fdTo = to[1];
8453     *pr = (ProcRef) cp;
8454
8455     return 0;
8456 }
8457
8458 int OpenRcmd(host, user, cmd, pr)
8459      char *host, *user, *cmd;
8460      ProcRef *pr;
8461 {
8462     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
8463     return -1;
8464 }
8465
8466 #define INPUT_SOURCE_BUF_SIZE 8192
8467
8468 typedef struct {
8469     CPKind kind;
8470     int fd;
8471     int lineByLine;
8472     char *unused;
8473     InputCallback func;
8474     XtInputId xid;
8475     char buf[INPUT_SOURCE_BUF_SIZE];
8476     VOIDSTAR closure;
8477 } InputSource;
8478
8479 void
8480 DoInputCallback(closure, source, xid)
8481      caddr_t closure;
8482      int *source;
8483      XtInputId *xid;
8484 {
8485     InputSource *is = (InputSource *) closure;
8486     int count;
8487     int error;
8488     char *p, *q;
8489
8490     if (is->lineByLine) {
8491         count = read(is->fd, is->unused,
8492                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
8493         if (count <= 0) {
8494             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
8495             return;
8496         }
8497         is->unused += count;
8498         p = is->buf;
8499         while (p < is->unused) {
8500             q = memchr(p, '\n', is->unused - p);
8501             if (q == NULL) break;
8502             q++;
8503             (is->func)(is, is->closure, p, q - p, 0);
8504             p = q;
8505         }
8506         q = is->buf;
8507         while (p < is->unused) {
8508             *q++ = *p++;
8509         }
8510         is->unused = q;
8511     } else {
8512         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
8513         if (count == -1)
8514           error = errno;
8515         else
8516           error = 0;
8517         (is->func)(is, is->closure, is->buf, count, error);
8518     }
8519 }
8520
8521 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
8522      ProcRef pr;
8523      int lineByLine;
8524      InputCallback func;
8525      VOIDSTAR closure;
8526 {
8527     InputSource *is;
8528     ChildProc *cp = (ChildProc *) pr;
8529
8530     is = (InputSource *) calloc(1, sizeof(InputSource));
8531     is->lineByLine = lineByLine;
8532     is->func = func;
8533     if (pr == NoProc) {
8534         is->kind = CPReal;
8535         is->fd = fileno(stdin);
8536     } else {
8537         is->kind = cp->kind;
8538         is->fd = cp->fdFrom;
8539     }
8540     if (lineByLine) {
8541         is->unused = is->buf;
8542     }
8543
8544     is->xid = XtAppAddInput(appContext, is->fd,
8545                             (XtPointer) (XtInputReadMask),
8546                             (XtInputCallbackProc) DoInputCallback,
8547                             (XtPointer) is);
8548     is->closure = closure;
8549     return (InputSourceRef) is;
8550 }
8551
8552 void
8553 RemoveInputSource(isr)
8554      InputSourceRef isr;
8555 {
8556     InputSource *is = (InputSource *) isr;
8557
8558     if (is->xid == 0) return;
8559     XtRemoveInput(is->xid);
8560     is->xid = 0;
8561 }
8562
8563 int OutputToProcess(pr, message, count, outError)
8564      ProcRef pr;
8565      char *message;
8566      int count;
8567      int *outError;
8568 {
8569     static int line = 0;
8570     ChildProc *cp = (ChildProc *) pr;
8571     int outCount;
8572
8573     if (pr == NoProc)
8574     {
8575         if (appData.noJoin || !appData.useInternalWrap)
8576             outCount = fwrite(message, 1, count, stdout);
8577         else
8578         {
8579             int width = get_term_width();
8580             int len = wrap(NULL, message, count, width, &line);
8581             char *msg = malloc(len);
8582             int dbgchk;
8583
8584             if (!msg)
8585                 outCount = fwrite(message, 1, count, stdout);
8586             else
8587             {
8588                 dbgchk = wrap(msg, message, count, width, &line);
8589                 if (dbgchk != len && appData.debugMode)
8590                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
8591                 outCount = fwrite(msg, 1, dbgchk, stdout);
8592                 free(msg);
8593             }
8594         }
8595     }
8596     else
8597       outCount = write(cp->fdTo, message, count);
8598
8599     if (outCount == -1)
8600       *outError = errno;
8601     else
8602       *outError = 0;
8603
8604     return outCount;
8605 }
8606
8607 /* Output message to process, with "ms" milliseconds of delay
8608    between each character. This is needed when sending the logon
8609    script to ICC, which for some reason doesn't like the
8610    instantaneous send. */
8611 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8612      ProcRef pr;
8613      char *message;
8614      int count;
8615      int *outError;
8616      long msdelay;
8617 {
8618     ChildProc *cp = (ChildProc *) pr;
8619     int outCount = 0;
8620     int r;
8621
8622     while (count--) {
8623         r = write(cp->fdTo, message++, 1);
8624         if (r == -1) {
8625             *outError = errno;
8626             return outCount;
8627         }
8628         ++outCount;
8629         if (msdelay >= 0)
8630           TimeDelay(msdelay);
8631     }
8632
8633     return outCount;
8634 }
8635
8636 /****   Animation code by Hugh Fisher, DCS, ANU.
8637
8638         Known problem: if a window overlapping the board is
8639         moved away while a piece is being animated underneath,
8640         the newly exposed area won't be updated properly.
8641         I can live with this.
8642
8643         Known problem: if you look carefully at the animation
8644         of pieces in mono mode, they are being drawn as solid
8645         shapes without interior detail while moving. Fixing
8646         this would be a major complication for minimal return.
8647 ****/
8648
8649 /*      Masks for XPM pieces. Black and white pieces can have
8650         different shapes, but in the interest of retaining my
8651         sanity pieces must have the same outline on both light
8652         and dark squares, and all pieces must use the same
8653         background square colors/images.                */
8654
8655 static int xpmDone = 0;
8656
8657 static void
8658 CreateAnimMasks (pieceDepth)
8659      int pieceDepth;
8660 {
8661   ChessSquare   piece;
8662   Pixmap        buf;
8663   GC            bufGC, maskGC;
8664   int           kind, n;
8665   unsigned long plane;
8666   XGCValues     values;
8667
8668   /* Need a bitmap just to get a GC with right depth */
8669   buf = XCreatePixmap(xDisplay, xBoardWindow,
8670                         8, 8, 1);
8671   values.foreground = 1;
8672   values.background = 0;
8673   /* Don't use XtGetGC, not read only */
8674   maskGC = XCreateGC(xDisplay, buf,
8675                     GCForeground | GCBackground, &values);
8676   XFreePixmap(xDisplay, buf);
8677
8678   buf = XCreatePixmap(xDisplay, xBoardWindow,
8679                       squareSize, squareSize, pieceDepth);
8680   values.foreground = XBlackPixel(xDisplay, xScreen);
8681   values.background = XWhitePixel(xDisplay, xScreen);
8682   bufGC = XCreateGC(xDisplay, buf,
8683                     GCForeground | GCBackground, &values);
8684
8685   for (piece = WhitePawn; piece <= BlackKing; piece++) {
8686     /* Begin with empty mask */
8687     if(!xpmDone) // [HGM] pieces: keep using existing
8688     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8689                                  squareSize, squareSize, 1);
8690     XSetFunction(xDisplay, maskGC, GXclear);
8691     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8692                    0, 0, squareSize, squareSize);
8693
8694     /* Take a copy of the piece */
8695     if (White(piece))
8696       kind = 0;
8697     else
8698       kind = 2;
8699     XSetFunction(xDisplay, bufGC, GXcopy);
8700     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8701               buf, bufGC,
8702               0, 0, squareSize, squareSize, 0, 0);
8703
8704     /* XOR the background (light) over the piece */
8705     XSetFunction(xDisplay, bufGC, GXxor);
8706     if (useImageSqs)
8707       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8708                 0, 0, squareSize, squareSize, 0, 0);
8709     else {
8710       XSetForeground(xDisplay, bufGC, lightSquareColor);
8711       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8712     }
8713
8714     /* We now have an inverted piece image with the background
8715        erased. Construct mask by just selecting all the non-zero
8716        pixels - no need to reconstruct the original image.      */
8717     XSetFunction(xDisplay, maskGC, GXor);
8718     plane = 1;
8719     /* Might be quicker to download an XImage and create bitmap
8720        data from it rather than this N copies per piece, but it
8721        only takes a fraction of a second and there is a much
8722        longer delay for loading the pieces.             */
8723     for (n = 0; n < pieceDepth; n ++) {
8724       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8725                  0, 0, squareSize, squareSize,
8726                  0, 0, plane);
8727       plane = plane << 1;
8728     }
8729   }
8730   /* Clean up */
8731   XFreePixmap(xDisplay, buf);
8732   XFreeGC(xDisplay, bufGC);
8733   XFreeGC(xDisplay, maskGC);
8734 }
8735
8736 static void
8737 InitAnimState (anim, info)
8738   AnimState * anim;
8739   XWindowAttributes * info;
8740 {
8741   XtGCMask  mask;
8742   XGCValues values;
8743
8744   /* Each buffer is square size, same depth as window */
8745   anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8746                         squareSize, squareSize, info->depth);
8747   anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8748                         squareSize, squareSize, info->depth);
8749
8750   /* Create a plain GC for blitting */
8751   mask = GCForeground | GCBackground | GCFunction |
8752          GCPlaneMask | GCGraphicsExposures;
8753   values.foreground = XBlackPixel(xDisplay, xScreen);
8754   values.background = XWhitePixel(xDisplay, xScreen);
8755   values.function   = GXcopy;
8756   values.plane_mask = AllPlanes;
8757   values.graphics_exposures = False;
8758   anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8759
8760   /* Piece will be copied from an existing context at
8761      the start of each new animation/drag. */
8762   anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8763
8764   /* Outline will be a read-only copy of an existing */
8765   anim->outlineGC = None;
8766 }
8767
8768 static void
8769 CreateAnimVars ()
8770 {
8771   static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
8772   XWindowAttributes info;
8773
8774   if (xpmDone && gameInfo.variant == old) return;
8775   if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
8776   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8777
8778   InitAnimState(&game, &info);
8779   InitAnimState(&player, &info);
8780
8781   /* For XPM pieces, we need bitmaps to use as masks. */
8782   if (useImages)
8783     CreateAnimMasks(info.depth);
8784    xpmDone = 1;
8785 }
8786
8787 #ifndef HAVE_USLEEP
8788
8789 static Boolean frameWaiting;
8790
8791 static RETSIGTYPE FrameAlarm (sig)
8792      int sig;
8793 {
8794   frameWaiting = False;
8795   /* In case System-V style signals.  Needed?? */
8796   signal(SIGALRM, FrameAlarm);
8797 }
8798
8799 static void
8800 FrameDelay (time)
8801      int time;
8802 {
8803   struct itimerval delay;
8804
8805   XSync(xDisplay, False);
8806
8807   if (time > 0) {
8808     frameWaiting = True;
8809     signal(SIGALRM, FrameAlarm);
8810     delay.it_interval.tv_sec =
8811       delay.it_value.tv_sec = time / 1000;
8812     delay.it_interval.tv_usec =
8813       delay.it_value.tv_usec = (time % 1000) * 1000;
8814     setitimer(ITIMER_REAL, &delay, NULL);
8815     while (frameWaiting) pause();
8816     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8817     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8818     setitimer(ITIMER_REAL, &delay, NULL);
8819   }
8820 }
8821
8822 #else
8823
8824 static void
8825 FrameDelay (time)
8826      int time;
8827 {
8828   XSync(xDisplay, False);
8829   if (time > 0)
8830     usleep(time * 1000);
8831 }
8832
8833 #endif
8834
8835 /*      Convert board position to corner of screen rect and color       */
8836
8837 static void
8838 ScreenSquare(column, row, pt, color)
8839      int column; int row; XPoint * pt; int * color;
8840 {
8841   if (flipView) {
8842     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8843     pt->y = lineGap + row * (squareSize + lineGap);
8844   } else {
8845     pt->x = lineGap + column * (squareSize + lineGap);
8846     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8847   }
8848   *color = SquareColor(row, column);
8849 }
8850
8851 /*      Convert window coords to square                 */
8852
8853 static void
8854 BoardSquare(x, y, column, row)
8855      int x; int y; int * column; int * row;
8856 {
8857   *column = EventToSquare(x, BOARD_WIDTH);
8858   if (flipView && *column >= 0)
8859     *column = BOARD_WIDTH - 1 - *column;
8860   *row = EventToSquare(y, BOARD_HEIGHT);
8861   if (!flipView && *row >= 0)
8862     *row = BOARD_HEIGHT - 1 - *row;
8863 }
8864
8865 /*   Utilities  */
8866
8867 #undef Max  /* just in case */
8868 #undef Min
8869 #define Max(a, b) ((a) > (b) ? (a) : (b))
8870 #define Min(a, b) ((a) < (b) ? (a) : (b))
8871
8872 static void
8873 SetRect(rect, x, y, width, height)
8874      XRectangle * rect; int x; int y; int width; int height;
8875 {
8876   rect->x = x;
8877   rect->y = y;
8878   rect->width  = width;
8879   rect->height = height;
8880 }
8881
8882 /*      Test if two frames overlap. If they do, return
8883         intersection rect within old and location of
8884         that rect within new. */
8885
8886 static Boolean
8887 Intersect(old, new, size, area, pt)
8888      XPoint * old; XPoint * new;
8889      int size; XRectangle * area; XPoint * pt;
8890 {
8891   if (old->x > new->x + size || new->x > old->x + size ||
8892       old->y > new->y + size || new->y > old->y + size) {
8893     return False;
8894   } else {
8895     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8896             size - abs(old->x - new->x), size - abs(old->y - new->y));
8897     pt->x = Max(old->x - new->x, 0);
8898     pt->y = Max(old->y - new->y, 0);
8899     return True;
8900   }
8901 }
8902
8903 /*      For two overlapping frames, return the rect(s)
8904         in the old that do not intersect with the new.   */
8905
8906 static void
8907 CalcUpdateRects(old, new, size, update, nUpdates)
8908      XPoint * old; XPoint * new; int size;
8909      XRectangle update[]; int * nUpdates;
8910 {
8911   int        count;
8912
8913   /* If old = new (shouldn't happen) then nothing to draw */
8914   if (old->x == new->x && old->y == new->y) {
8915     *nUpdates = 0;
8916     return;
8917   }
8918   /* Work out what bits overlap. Since we know the rects
8919      are the same size we don't need a full intersect calc. */
8920   count = 0;
8921   /* Top or bottom edge? */
8922   if (new->y > old->y) {
8923     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8924     count ++;
8925   } else if (old->y > new->y) {
8926     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8927                               size, old->y - new->y);
8928     count ++;
8929   }
8930   /* Left or right edge - don't overlap any update calculated above. */
8931   if (new->x > old->x) {
8932     SetRect(&(update[count]), old->x, Max(new->y, old->y),
8933                               new->x - old->x, size - abs(new->y - old->y));
8934     count ++;
8935   } else if (old->x > new->x) {
8936     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8937                               old->x - new->x, size - abs(new->y - old->y));
8938     count ++;
8939   }
8940   /* Done */
8941   *nUpdates = count;
8942 }
8943
8944 /*      Generate a series of frame coords from start->mid->finish.
8945         The movement rate doubles until the half way point is
8946         reached, then halves back down to the final destination,
8947         which gives a nice slow in/out effect. The algorithmn
8948         may seem to generate too many intermediates for short
8949         moves, but remember that the purpose is to attract the
8950         viewers attention to the piece about to be moved and
8951         then to where it ends up. Too few frames would be less
8952         noticeable.                                             */
8953
8954 static void
8955 Tween(start, mid, finish, factor, frames, nFrames)
8956      XPoint * start; XPoint * mid;
8957      XPoint * finish; int factor;
8958      XPoint frames[]; int * nFrames;
8959 {
8960   int fraction, n, count;
8961
8962   count = 0;
8963
8964   /* Slow in, stepping 1/16th, then 1/8th, ... */
8965   fraction = 1;
8966   for (n = 0; n < factor; n++)
8967     fraction *= 2;
8968   for (n = 0; n < factor; n++) {
8969     frames[count].x = start->x + (mid->x - start->x) / fraction;
8970     frames[count].y = start->y + (mid->y - start->y) / fraction;
8971     count ++;
8972     fraction = fraction / 2;
8973   }
8974
8975   /* Midpoint */
8976   frames[count] = *mid;
8977   count ++;
8978
8979   /* Slow out, stepping 1/2, then 1/4, ... */
8980   fraction = 2;
8981   for (n = 0; n < factor; n++) {
8982     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8983     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8984     count ++;
8985     fraction = fraction * 2;
8986   }
8987   *nFrames = count;
8988 }
8989
8990 /*      Draw a piece on the screen without disturbing what's there      */
8991
8992 static void
8993 SelectGCMask(piece, clip, outline, mask)
8994      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8995 {
8996   GC source;
8997
8998   /* Bitmap for piece being moved. */
8999   if (appData.monoMode) {
9000       *mask = *pieceToSolid(piece);
9001   } else if (useImages) {
9002 #if HAVE_LIBXPM
9003       *mask = xpmMask[piece];
9004 #else
9005       *mask = ximMaskPm[piece];
9006 #endif
9007   } else {
9008       *mask = *pieceToSolid(piece);
9009   }
9010
9011   /* GC for piece being moved. Square color doesn't matter, but
9012      since it gets modified we make a copy of the original. */
9013   if (White(piece)) {
9014     if (appData.monoMode)
9015       source = bwPieceGC;
9016     else
9017       source = wlPieceGC;
9018   } else {
9019     if (appData.monoMode)
9020       source = wbPieceGC;
9021     else
9022       source = blPieceGC;
9023   }
9024   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
9025
9026   /* Outline only used in mono mode and is not modified */
9027   if (White(piece))
9028     *outline = bwPieceGC;
9029   else
9030     *outline = wbPieceGC;
9031 }
9032
9033 static void
9034 OverlayPiece(piece, clip, outline,  dest)
9035      ChessSquare piece; GC clip; GC outline; Drawable dest;
9036 {
9037   int   kind;
9038
9039   if (!useImages) {
9040     /* Draw solid rectangle which will be clipped to shape of piece */
9041     XFillRectangle(xDisplay, dest, clip,
9042                    0, 0, squareSize, squareSize);
9043     if (appData.monoMode)
9044       /* Also draw outline in contrasting color for black
9045          on black / white on white cases                */
9046       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
9047                  0, 0, squareSize, squareSize, 0, 0, 1);
9048   } else {
9049     /* Copy the piece */
9050     if (White(piece))
9051       kind = 0;
9052     else
9053       kind = 2;
9054     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
9055               dest, clip,
9056               0, 0, squareSize, squareSize,
9057               0, 0);
9058   }
9059 }
9060
9061 /* Animate the movement of a single piece */
9062
9063 static void
9064 BeginAnimation(anim, piece, startColor, start)
9065      AnimState *anim;
9066      ChessSquare piece;
9067      int startColor;
9068      XPoint * start;
9069 {
9070   Pixmap mask;
9071
9072   /* The old buffer is initialised with the start square (empty) */
9073   BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
9074   anim->prevFrame = *start;
9075
9076   /* The piece will be drawn using its own bitmap as a matte    */
9077   SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
9078   XSetClipMask(xDisplay, anim->pieceGC, mask);
9079 }
9080
9081 static void
9082 AnimationFrame(anim, frame, piece)
9083      AnimState *anim;
9084      XPoint *frame;
9085      ChessSquare piece;
9086 {
9087   XRectangle updates[4];
9088   XRectangle overlap;
9089   XPoint     pt;
9090   int        count, i;
9091
9092   /* Save what we are about to draw into the new buffer */
9093   XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
9094             frame->x, frame->y, squareSize, squareSize,
9095             0, 0);
9096
9097   /* Erase bits of the previous frame */
9098   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
9099     /* Where the new frame overlapped the previous,
9100        the contents in newBuf are wrong. */
9101     XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
9102               overlap.x, overlap.y,
9103               overlap.width, overlap.height,
9104               pt.x, pt.y);
9105     /* Repaint the areas in the old that don't overlap new */
9106     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
9107     for (i = 0; i < count; i++)
9108       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
9109                 updates[i].x - anim->prevFrame.x,
9110                 updates[i].y - anim->prevFrame.y,
9111                 updates[i].width, updates[i].height,
9112                 updates[i].x, updates[i].y);
9113   } else {
9114     /* Easy when no overlap */
9115     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
9116                   0, 0, squareSize, squareSize,
9117                   anim->prevFrame.x, anim->prevFrame.y);
9118   }
9119
9120   /* Save this frame for next time round */
9121   XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
9122                 0, 0, squareSize, squareSize,
9123                 0, 0);
9124   anim->prevFrame = *frame;
9125
9126   /* Draw piece over original screen contents, not current,
9127      and copy entire rect. Wipes out overlapping piece images. */
9128   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
9129   XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
9130                 0, 0, squareSize, squareSize,
9131                 frame->x, frame->y);
9132 }
9133
9134 static void
9135 EndAnimation (anim, finish)
9136      AnimState *anim;
9137      XPoint *finish;
9138 {
9139   XRectangle updates[4];
9140   XRectangle overlap;
9141   XPoint     pt;
9142   int        count, i;
9143
9144   /* The main code will redraw the final square, so we
9145      only need to erase the bits that don't overlap.    */
9146   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
9147     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
9148     for (i = 0; i < count; i++)
9149       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
9150                 updates[i].x - anim->prevFrame.x,
9151                 updates[i].y - anim->prevFrame.y,
9152                 updates[i].width, updates[i].height,
9153                 updates[i].x, updates[i].y);
9154   } else {
9155     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
9156                 0, 0, squareSize, squareSize,
9157                 anim->prevFrame.x, anim->prevFrame.y);
9158   }
9159 }
9160
9161 static void
9162 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
9163      AnimState *anim;
9164      ChessSquare piece; int startColor;
9165      XPoint * start; XPoint * finish;
9166      XPoint frames[]; int nFrames;
9167 {
9168   int n;
9169
9170   BeginAnimation(anim, piece, startColor, start);
9171   for (n = 0; n < nFrames; n++) {
9172     AnimationFrame(anim, &(frames[n]), piece);
9173     FrameDelay(appData.animSpeed);
9174   }
9175   EndAnimation(anim, finish);
9176 }
9177
9178 /* Main control logic for deciding what to animate and how */
9179
9180 void
9181 AnimateMove(board, fromX, fromY, toX, toY)
9182      Board board;
9183      int fromX;
9184      int fromY;
9185      int toX;
9186      int toY;
9187 {
9188   ChessSquare piece;
9189   int hop;
9190   XPoint      start, finish, mid;
9191   XPoint      frames[kFactor * 2 + 1];
9192   int         nFrames, startColor, endColor;
9193
9194   /* Are we animating? */
9195   if (!appData.animate || appData.blindfold)
9196     return;
9197
9198   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing || 
9199      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing) 
9200         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
9201
9202   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
9203   piece = board[fromY][fromX];
9204   if (piece >= EmptySquare) return;
9205
9206 #if DONT_HOP
9207   hop = FALSE;
9208 #else
9209   hop = (piece == WhiteKnight || piece == BlackKnight);
9210 #endif
9211
9212   if (appData.debugMode) {
9213       fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
9214                              _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
9215              piece, fromX, fromY, toX, toY);  }
9216
9217   ScreenSquare(fromX, fromY, &start, &startColor);
9218   ScreenSquare(toX, toY, &finish, &endColor);
9219
9220   if (hop) {
9221     /* Knight: make diagonal movement then straight */
9222     if (abs(toY - fromY) < abs(toX - fromX)) {
9223        mid.x = start.x + (finish.x - start.x) / 2;
9224        mid.y = finish.y;
9225      } else {
9226        mid.x = finish.x;
9227        mid.y = start.y + (finish.y - start.y) / 2;
9228      }
9229   } else {
9230     mid.x = start.x + (finish.x - start.x) / 2;
9231     mid.y = start.y + (finish.y - start.y) / 2;
9232   }
9233
9234   /* Don't use as many frames for very short moves */
9235   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
9236     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
9237   else
9238     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
9239   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
9240
9241   /* Be sure end square is redrawn */
9242   damage[toY][toX] = True;
9243 }
9244
9245 void
9246 DragPieceBegin(x, y)
9247      int x; int y;
9248 {
9249     int  boardX, boardY, color;
9250     XPoint corner;
9251
9252     /* Are we animating? */
9253     if (!appData.animateDragging || appData.blindfold)
9254       return;
9255
9256     /* Figure out which square we start in and the
9257        mouse position relative to top left corner. */
9258     BoardSquare(x, y, &boardX, &boardY);
9259     player.startBoardX = boardX;
9260     player.startBoardY = boardY;
9261     ScreenSquare(boardX, boardY, &corner, &color);
9262     player.startSquare  = corner;
9263     player.startColor   = color;
9264     /* As soon as we start dragging, the piece will jump slightly to
9265        be centered over the mouse pointer. */
9266     player.mouseDelta.x = squareSize/2;
9267     player.mouseDelta.y = squareSize/2;
9268     /* Initialise animation */
9269     player.dragPiece = PieceForSquare(boardX, boardY);
9270     /* Sanity check */
9271     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
9272         player.dragActive = True;
9273         BeginAnimation(&player, player.dragPiece, color, &corner);
9274         /* Mark this square as needing to be redrawn. Note that
9275            we don't remove the piece though, since logically (ie
9276            as seen by opponent) the move hasn't been made yet. */
9277            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
9278               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
9279            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
9280                      corner.x, corner.y, squareSize, squareSize,
9281                      0, 0); // [HGM] zh: unstack in stead of grab
9282         damage[boardY][boardX] = True;
9283     } else {
9284         player.dragActive = False;
9285     }
9286 }
9287
9288 static void
9289 DragPieceMove(x, y)
9290      int x; int y;
9291 {
9292     XPoint corner;
9293
9294     /* Are we animating? */
9295     if (!appData.animateDragging || appData.blindfold)
9296       return;
9297
9298     /* Sanity check */
9299     if (! player.dragActive)
9300       return;
9301     /* Move piece, maintaining same relative position
9302        of mouse within square    */
9303     corner.x = x - player.mouseDelta.x;
9304     corner.y = y - player.mouseDelta.y;
9305     AnimationFrame(&player, &corner, player.dragPiece);
9306 #if HIGHDRAG
9307     if (appData.highlightDragging) {
9308         int boardX, boardY;
9309         BoardSquare(x, y, &boardX, &boardY);
9310         SetHighlights(fromX, fromY, boardX, boardY);
9311     }
9312 #endif
9313 }
9314
9315 void
9316 DragPieceEnd(x, y)
9317      int x; int y;
9318 {
9319     int boardX, boardY, color;
9320     XPoint corner;
9321
9322     /* Are we animating? */
9323     if (!appData.animateDragging || appData.blindfold)
9324       return;
9325
9326     /* Sanity check */
9327     if (! player.dragActive)
9328       return;
9329     /* Last frame in sequence is square piece is
9330        placed on, which may not match mouse exactly. */
9331     BoardSquare(x, y, &boardX, &boardY);
9332     ScreenSquare(boardX, boardY, &corner, &color);
9333     EndAnimation(&player, &corner);
9334
9335     /* Be sure end square is redrawn */
9336     damage[boardY][boardX] = True;
9337
9338     /* This prevents weird things happening with fast successive
9339        clicks which on my Sun at least can cause motion events
9340        without corresponding press/release. */
9341     player.dragActive = False;
9342 }
9343
9344 /* Handle expose event while piece being dragged */
9345
9346 static void
9347 DrawDragPiece ()
9348 {
9349   if (!player.dragActive || appData.blindfold)
9350     return;
9351
9352   /* What we're doing: logically, the move hasn't been made yet,
9353      so the piece is still in it's original square. But visually
9354      it's being dragged around the board. So we erase the square
9355      that the piece is on and draw it at the last known drag point. */
9356   BlankSquare(player.startSquare.x, player.startSquare.y,
9357                 player.startColor, EmptySquare, xBoardWindow);
9358   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
9359   damage[player.startBoardY][player.startBoardX] = TRUE;
9360 }
9361
9362 void
9363 SetProgramStats( FrontEndProgramStats * stats )
9364 {
9365   // [HR] TODO
9366   // [HGM] done, but perhaps backend should call this directly?
9367     EngineOutputUpdate( stats );
9368 }
9369
9370 #include <sys/ioctl.h>
9371 int get_term_width()
9372 {
9373     int fd, default_width;
9374
9375     fd = STDIN_FILENO;
9376     default_width = 79; // this is FICS default anyway...
9377
9378 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
9379     struct ttysize win;
9380     if (!ioctl(fd, TIOCGSIZE, &win))
9381         default_width = win.ts_cols;
9382 #elif defined(TIOCGWINSZ)
9383     struct winsize win;
9384     if (!ioctl(fd, TIOCGWINSZ, &win))
9385         default_width = win.ws_col;
9386 #endif
9387     return default_width;
9388 }
9389
9390 void update_ics_width()
9391 {
9392     static int old_width = 0;
9393     int new_width = get_term_width();
9394
9395     if (old_width != new_width)
9396        ics_printf("set width %d\n", new_width);
9397     old_width = new_width;
9398 }
9399
9400 void NotifyFrontendLogin()
9401 {
9402     update_ics_width();
9403 }