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