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