improves the XBoard handling of the engine command line
[xboard.git] / xboard.c
1 /*
2  * xboard.c -- X front end for XBoard
3  *
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,
5  * Massachusetts. 
6  *
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8  * 2007, 2008, 2009 Free Software Foundation, Inc.
9  *
10  * The following terms apply to Digital Equipment Corporation's copyright
11  * interest in XBoard:
12  * ------------------------------------------------------------------------
13  * All Rights Reserved
14  *
15  * Permission to use, copy, modify, and distribute this software and its
16  * documentation for any purpose and without fee is hereby granted,
17  * provided that the above copyright notice appear in all copies and that
18  * both that copyright notice and this permission notice appear in
19  * supporting documentation, and that the name of Digital not be
20  * used in advertising or publicity pertaining to distribution of the
21  * software without specific, written prior permission.
22  *
23  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29  * SOFTWARE.
30  * ------------------------------------------------------------------------
31  *
32  * The following terms apply to the enhanced version of XBoard
33  * distributed by the Free Software Foundation:
34  * ------------------------------------------------------------------------
35  *
36  * GNU XBoard is free software: you can redistribute it and/or modify
37  * it under the terms of the GNU General Public License as published by
38  * the Free Software Foundation, either version 3 of the License, or (at
39  * your option) any later version.
40  *
41  * GNU XBoard is distributed in the hope that it will be useful, but
42  * WITHOUT ANY WARRANTY; without even the implied warranty of
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44  * General Public License for more details.
45  *
46  * You should have received a copy of the GNU General Public License
47  * along with this program. If not, see http://www.gnu.org/licenses/.  *
48  *
49  *------------------------------------------------------------------------
50  ** See the file ChangeLog for a revision history.  */
51
52 #include "config.h"
53
54 #include <stdio.h>
55 #include <ctype.h>
56 #include <signal.h>
57 #include <errno.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <pwd.h>
61
62 #if !OMIT_SOCKETS
63 # if HAVE_SYS_SOCKET_H
64 #  include <sys/socket.h>
65 #  include <netinet/in.h>
66 #  include <netdb.h>
67 # else /* not HAVE_SYS_SOCKET_H */
68 #  if HAVE_LAN_SOCKET_H
69 #   include <lan/socket.h>
70 #   include <lan/in.h>
71 #   include <lan/netdb.h>
72 #  else /* not HAVE_LAN_SOCKET_H */
73 #   define OMIT_SOCKETS 1
74 #  endif /* not HAVE_LAN_SOCKET_H */
75 # endif /* not HAVE_SYS_SOCKET_H */
76 #endif /* !OMIT_SOCKETS */
77
78 #if STDC_HEADERS
79 # include <stdlib.h>
80 # include <string.h>
81 #else /* not STDC_HEADERS */
82 extern char *getenv();
83 # if HAVE_STRING_H
84 #  include <string.h>
85 # else /* not HAVE_STRING_H */
86 #  include <strings.h>
87 # endif /* not HAVE_STRING_H */
88 #endif /* not STDC_HEADERS */
89
90 #if HAVE_SYS_FCNTL_H
91 # include <sys/fcntl.h>
92 #else /* not HAVE_SYS_FCNTL_H */
93 # if HAVE_FCNTL_H
94 #  include <fcntl.h>
95 # endif /* HAVE_FCNTL_H */
96 #endif /* not HAVE_SYS_FCNTL_H */
97
98 #if HAVE_SYS_SYSTEMINFO_H
99 # include <sys/systeminfo.h>
100 #endif /* HAVE_SYS_SYSTEMINFO_H */
101
102 #if TIME_WITH_SYS_TIME
103 # include <sys/time.h>
104 # include <time.h>
105 #else
106 # if HAVE_SYS_TIME_H
107 #  include <sys/time.h>
108 # else
109 #  include <time.h>
110 # endif
111 #endif
112
113 #if HAVE_UNISTD_H
114 # include <unistd.h>
115 #endif
116
117 #if HAVE_SYS_WAIT_H
118 # include <sys/wait.h>
119 #endif
120
121 #if HAVE_DIRENT_H
122 # include <dirent.h>
123 # define NAMLEN(dirent) strlen((dirent)->d_name)
124 # define HAVE_DIR_STRUCT
125 #else
126 # define dirent direct
127 # define NAMLEN(dirent) (dirent)->d_namlen
128 # if HAVE_SYS_NDIR_H
129 #  include <sys/ndir.h>
130 #  define HAVE_DIR_STRUCT
131 # endif
132 # if HAVE_SYS_DIR_H
133 #  include <sys/dir.h>
134 #  define HAVE_DIR_STRUCT
135 # endif
136 # if HAVE_NDIR_H
137 #  include <ndir.h>
138 #  define HAVE_DIR_STRUCT
139 # endif
140 #endif
141
142 #include <X11/Intrinsic.h>
143 #include <X11/StringDefs.h>
144 #include <X11/Shell.h>
145 #include <X11/cursorfont.h>
146 #include <X11/Xatom.h>
147 #include <X11/Xmu/Atoms.h>
148 #if USE_XAW3D
149 #include <X11/Xaw3d/Dialog.h>
150 #include <X11/Xaw3d/Form.h>
151 #include <X11/Xaw3d/List.h>
152 #include <X11/Xaw3d/Label.h>
153 #include <X11/Xaw3d/SimpleMenu.h>
154 #include <X11/Xaw3d/SmeBSB.h>
155 #include <X11/Xaw3d/SmeLine.h>
156 #include <X11/Xaw3d/Box.h>
157 #include <X11/Xaw3d/MenuButton.h>
158 #include <X11/Xaw3d/Text.h>
159 #include <X11/Xaw3d/AsciiText.h>
160 #else
161 #include <X11/Xaw/Dialog.h>
162 #include <X11/Xaw/Form.h>
163 #include <X11/Xaw/List.h>
164 #include <X11/Xaw/Label.h>
165 #include <X11/Xaw/SimpleMenu.h>
166 #include <X11/Xaw/SmeBSB.h>
167 #include <X11/Xaw/SmeLine.h>
168 #include <X11/Xaw/Box.h>
169 #include <X11/Xaw/MenuButton.h>
170 #include <X11/Xaw/Text.h>
171 #include <X11/Xaw/AsciiText.h>
172 #endif
173
174 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
175 #include "common.h"
176
177 #if HAVE_LIBXPM
178 #include <X11/xpm.h>
179 #include "pixmaps/pixmaps.h"
180 #define IMAGE_EXT "xpm"
181 #else
182 #define IMAGE_EXT "xim"
183 #include "bitmaps/bitmaps.h"
184 #endif
185
186 #include "bitmaps/icon_white.bm"
187 #include "bitmaps/icon_black.bm"
188 #include "bitmaps/checkmark.bm"
189
190 #include "frontend.h"
191 #include "backend.h"
192 #include "moves.h"
193 #include "xboard.h"
194 #include "childio.h"
195 #include "xgamelist.h"
196 #include "xhistory.h"
197 #include "xedittags.h"
198 #include "gettext.h"
199
200 // must be moved to xengineoutput.h
201
202 void EngineOutputProc P((Widget w, XEvent *event,
203  String *prms, Cardinal *nprms));
204
205
206 #ifdef __EMX__
207 #ifndef HAVE_USLEEP
208 #define HAVE_USLEEP
209 #endif
210 #define usleep(t)   _sleep2(((t)+500)/1000)
211 #endif
212
213 #ifdef ENABLE_NLS
214 # define  _(s) gettext (s)
215 # define N_(s) gettext_noop (s)
216 #else
217 # define  _(s) (s)
218 # define N_(s)  s
219 #endif
220
221 typedef struct {
222     String string;
223     XtActionProc proc;
224 } MenuItem;
225
226 typedef struct {
227     String name;
228     MenuItem *mi;
229 } Menu;
230
231 int main P((int argc, char **argv));
232 RETSIGTYPE CmailSigHandler P((int sig));
233 RETSIGTYPE IntSigHandler P((int sig));
234 RETSIGTYPE TermSizeSigHandler 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 PromotionPopDown P((void));
284 void PromotionCallback P((Widget w, XtPointer client_data,
285                           XtPointer call_data));
286 void EditCommentPopDown P((void));
287 void EditCommentCallback P((Widget w, XtPointer client_data,
288                             XtPointer call_data));
289 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
290 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
291 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
292 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
293                          Cardinal *nprms));
294 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
295                          Cardinal *nprms));
296 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
297                        Cardinal *nprms));
298 void LoadPositionProc P((Widget w, XEvent *event,
299                          String *prms, Cardinal *nprms));
300 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
301                          Cardinal *nprms));
302 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
303                          Cardinal *nprms));
304 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
305                        Cardinal *nprms));
306 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
307                          Cardinal *nprms));
308 void PastePositionProc P((Widget w, XEvent *event, String *prms,
309                           Cardinal *nprms));
310 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
311 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
312 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
313 void SavePositionProc P((Widget w, XEvent *event,
314                          String *prms, Cardinal *nprms));
315 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
316 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
317                             Cardinal *nprms));
318 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
319 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
320 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
321                          Cardinal *nprms));
322 void MachineWhiteProc P((Widget w, XEvent *event,
323                          String *prms, Cardinal *nprms));
324 void AnalyzeModeProc P((Widget w, XEvent *event,
325                          String *prms, Cardinal *nprms));
326 void AnalyzeFileProc P((Widget w, XEvent *event,
327                          String *prms, Cardinal *nprms));
328 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
329                         Cardinal *nprms));
330 void IcsClientProc P((Widget w, XEvent *event, String *prms,
331                       Cardinal *nprms));
332 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
333 void EditPositionProc P((Widget w, XEvent *event,
334                          String *prms, Cardinal *nprms));
335 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
336 void EditCommentProc P((Widget w, XEvent *event,
337                         String *prms, Cardinal *nprms));
338 void IcsInputBoxProc P((Widget w, XEvent *event,
339                         String *prms, Cardinal *nprms));
340 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
341 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
342 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
343 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
344 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
345 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
346 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
347 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
348 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
349 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
350 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
351 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
352 void StopObservingProc P((Widget w, XEvent *event, String *prms,
353                           Cardinal *nprms));
354 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
355                           Cardinal *nprms));
356 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
357 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
358 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
359 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
362                          Cardinal *nprms));
363 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
364                         Cardinal *nprms));
365 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
367                         Cardinal *nprms));
368 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
369                          Cardinal *nprms));
370 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
371                          Cardinal *nprms));
372 void AutocommProc P((Widget w, XEvent *event, String *prms,
373                      Cardinal *nprms));
374 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void AutobsProc P((Widget w, XEvent *event, String *prms,
377                         Cardinal *nprms));
378 void AutoraiseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void AutosaveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
382                        Cardinal *nprms));
383 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
384 void GetMoveListProc P((Widget w, XEvent *event, String *prms,
385                         Cardinal *nprms));
386 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
387                               Cardinal *nprms));
388 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
389                               Cardinal *nprms));
390 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
391 void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
392 void OldSaveStyleProc P((Widget w, XEvent *event, String *prms,
393                          Cardinal *nprms));
394 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
395                          Cardinal *nprms));
396 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
397                            Cardinal *nprms));
398 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
399                         Cardinal *nprms));
400 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
401                              Cardinal *nprms));
402 void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
403 void QuietPlayProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
404 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
405                        Cardinal *nprms));
406 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
407                          Cardinal *nprms));
408 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
409                          Cardinal *nprms));
410 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
411                           Cardinal *nprms));
412 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
413 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
414 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
415 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
416 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
417 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
418 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
419 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
420 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
421 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
422 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
423 void DisplayMove P((int moveNumber));
424 void DisplayTitle P((char *title));
425 void ICSInitScript P((void));
426 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
427 void ErrorPopUp P((char *title, char *text, int modal));
428 void ErrorPopDown P((void));
429 static char *ExpandPathName P((char *path));
430 static void CreateAnimVars P((void));
431 static void DragPieceMove P((int x, int y));
432 static void DrawDragPiece P((void));
433 char *ModeToWidgetName P((GameMode mode));
434 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
435 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
436 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
437 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
438 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
439 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
440 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
441 void ShufflePopDown P(());
442 void EnginePopDown P(());
443 void UciPopDown P(());
444 void TimeControlPopDown P(());
445 void NewVariantPopDown P(());
446 void SettingsPopDown P(());
447 void update_ics_width P(());
448 int get_term_width 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 Widget historyShell, evalGraphShell, gameListShell;
470 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
471 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
472 Font clockFontID, coordFontID, countFontID;
473 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
474 XtAppContext appContext;
475 char *layoutName;
476 char *oldICSInteractionTitle;
477
478 FileProc fileProc;
479 char *fileOpenMode;
480 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
481
482 Position commentX = -1, commentY = -1;
483 Dimension commentW, commentH;
484 typedef unsigned int BoardSize;
485 BoardSize boardSize;
486 Boolean chessProgram;
487
488 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
489 int squareSize, smallLayout = 0, tinyLayout = 0,
490   marginW, marginH, // [HGM] for run-time resizing
491   fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
492   ICSInputBoxUp = False, askQuestionUp = False,
493   filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
494   editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
495 Pixel timerForegroundPixel, timerBackgroundPixel;
496 Pixel buttonForegroundPixel, buttonBackgroundPixel;
497 char *chessDir, *programName, *programVersion,
498   *gameCopyFilename, *gamePasteFilename;
499 Boolean alwaysOnTop = False;
500 Boolean saveSettingsOnExit;
501 char *settingsFileName;
502 char *icsTextMenuString;
503 char *icsNames;
504 char *firstChessProgramNames;
505 char *secondChessProgramNames;
506
507 WindowPlacement wpMain;
508 WindowPlacement wpConsole;
509 WindowPlacement wpComment;
510 WindowPlacement wpMoveHistory;
511 WindowPlacement wpEvalGraph;
512 WindowPlacement wpEngineOutput;
513 WindowPlacement wpGameList;
514 WindowPlacement wpTags;
515
516 #define SOLID 0
517 #define OUTLINE 1
518 Pixmap pieceBitmap[2][(int)BlackPawn];
519 Pixmap pieceBitmap2[2][(int)BlackPawn+4];       /* [HGM] pieces */
520 Pixmap xpmPieceBitmap[4][(int)BlackPawn];       /* LL, LD, DL, DD actually used*/
521 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4];    /* LL, LD, DL, DD set to select from */
522 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
523 int useImages, useImageSqs;
524 XImage *ximPieceBitmap[4][(int)BlackPawn+4];    /* LL, LD, DL, DD */
525 Pixmap ximMaskPm[(int)BlackPawn];               /* clipmasks, used for XIM pieces */
526 Pixmap ximMaskPm2[(int)BlackPawn+4];            /* clipmasks, used for XIM pieces */
527 XImage *ximLightSquare, *ximDarkSquare;
528 XImage *xim_Cross;
529
530 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
531 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
532
533 #define White(piece) ((int)(piece) < (int)BlackPawn)
534
535 /* Variables for doing smooth animation. This whole thing
536    would be much easier if the board was double-buffered,
537    but that would require a fairly major rewrite.       */
538
539 typedef struct {
540         Pixmap  saveBuf;
541         Pixmap  newBuf;
542         GC      blitGC, pieceGC, outlineGC;
543         XPoint  startSquare, prevFrame, mouseDelta;
544         int     startColor;
545         int     dragPiece;
546         Boolean dragActive;
547         int     startBoardX, startBoardY;
548     } AnimState;
549
550 /* There can be two pieces being animated at once: a player
551    can begin dragging a piece before the remote opponent has moved. */
552
553 static AnimState game, player;
554
555 /* Bitmaps for use as masks when drawing XPM pieces.
556    Need one for each black and white piece.             */
557 static Pixmap xpmMask[BlackKing + 1];
558
559 /* This magic number is the number of intermediate frames used
560    in each half of the animation. For short moves it's reduced
561    by 1. The total number of frames will be factor * 2 + 1.  */
562 #define kFactor    4
563
564 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
565
566 MenuItem fileMenu[] = {
567     {N_("New Game"), ResetProc},
568     {N_("New Shuffle Game ..."), ShuffleMenuProc},
569     {N_("New Variant ..."), NewVariantProc},      // [HGM] variant: not functional yet
570     {"----", NothingProc},
571     {N_("Load Game"), LoadGameProc},
572     {N_("Load Next Game"), LoadNextGameProc},
573     {N_("Load Previous Game"), LoadPrevGameProc},
574     {N_("Reload Same Game"), ReloadGameProc},
575     {N_("Save Game"), SaveGameProc},
576     {"----", NothingProc},
577     {N_("Copy Game"), CopyGameProc},
578     {N_("Paste Game"), PasteGameProc},
579     {"----", NothingProc},
580     {N_("Load Position"), LoadPositionProc},
581     {N_("Load Next Position"), LoadNextPositionProc},
582     {N_("Load Previous Position"), LoadPrevPositionProc},
583     {N_("Reload Same Position"), ReloadPositionProc},
584     {N_("Save Position"), SavePositionProc},
585     {"----", NothingProc},
586     {N_("Copy Position"), CopyPositionProc},
587     {N_("Paste Position"), PastePositionProc},
588     {"----", NothingProc},
589     {N_("Mail Move"), MailMoveProc},
590     {N_("Reload CMail Message"), ReloadCmailMsgProc},
591     {"----", NothingProc},
592     {N_("Exit"), QuitProc},
593     {NULL, NULL}
594 };
595
596 MenuItem modeMenu[] = {
597     {N_("Machine White"), MachineWhiteProc},
598     {N_("Machine Black"), MachineBlackProc},
599     {N_("Two Machines"), TwoMachinesProc},
600     {N_("Analysis Mode"), AnalyzeModeProc},
601     {N_("Analyze File"), AnalyzeFileProc },
602     {N_("ICS Client"), IcsClientProc},
603     {N_("Edit Game"), EditGameProc},
604     {N_("Edit Position"), EditPositionProc},
605     {N_("Training"), TrainingProc},
606     {"----", NothingProc},
607     {N_("Show Engine Output"), EngineOutputProc},
608     {N_("Show Evaluation Graph"), NothingProc}, // [HGM] evalgr: not functional yet
609     {N_("Show Game List"), ShowGameListProc},
610     {"Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
611     {"----", NothingProc},
612     {N_("Edit Tags"), EditTagsProc},
613     {N_("Edit Comment"), EditCommentProc},
614     {N_("ICS Input Box"), IcsInputBoxProc},
615     {N_("Pause"), PauseProc},
616     {NULL, NULL}
617 };
618
619 MenuItem actionMenu[] = {
620     {N_("Accept"), AcceptProc},
621     {N_("Decline"), DeclineProc},
622     {N_("Rematch"), RematchProc},
623     {"----", NothingProc},
624     {N_("Call Flag"), CallFlagProc},
625     {N_("Draw"), DrawProc},
626     {N_("Adjourn"), AdjournProc},
627     {N_("Abort"), AbortProc},
628     {N_("Resign"), ResignProc},
629     {"----", NothingProc},
630     {N_("Stop Observing"), StopObservingProc},
631     {N_("Stop Examining"), StopExaminingProc},
632     {"----", NothingProc},
633     {N_("Adjudicate to White"), AdjuWhiteProc},
634     {N_("Adjudicate to Black"), AdjuBlackProc},
635     {N_("Adjudicate Draw"), AdjuDrawProc},
636     {NULL, NULL}
637 };
638
639 MenuItem stepMenu[] = {
640     {N_("Backward"), BackwardProc},
641     {N_("Forward"), ForwardProc},
642     {N_("Back to Start"), ToStartProc},
643     {N_("Forward to End"), ToEndProc},
644     {N_("Revert"), RevertProc},
645     {N_("Truncate Game"), TruncateGameProc},
646     {"----", NothingProc},
647     {N_("Move Now"), MoveNowProc},
648     {N_("Retract Move"), RetractMoveProc},
649     {NULL, NULL}
650 };
651
652 MenuItem optionsMenu[] = {
653     {N_("Flip View"), FlipViewProc},
654     {"----", NothingProc},
655     {N_("Adjudications ..."), EngineMenuProc},
656     {N_("General Settings ..."), UciMenuProc},
657     {N_("Engine #1 Settings ..."), FirstSettingsProc},
658     {N_("Engine #2 Settings ..."), SecondSettingsProc},
659     {N_("Time Control ..."), TimeControlProc},
660     {"----", NothingProc},
661     {N_("Always Queen"), AlwaysQueenProc},
662     {N_("Animate Dragging"), AnimateDraggingProc},
663     {N_("Animate Moving"), AnimateMovingProc},
664     {N_("Auto Comment"), AutocommProc},
665     {N_("Auto Flag"), AutoflagProc},
666     {N_("Auto Flip View"), AutoflipProc},
667     {N_("Auto Observe"), AutobsProc},
668     {N_("Auto Raise Board"), AutoraiseProc},
669     {N_("Auto Save"), AutosaveProc},
670     {N_("Blindfold"), BlindfoldProc},
671     {N_("Flash Moves"), FlashMovesProc},
672     {N_("Get Move List"), GetMoveListProc},
673 #if HIGHDRAG
674     {N_("Highlight Dragging"), HighlightDraggingProc},
675 #endif
676     {N_("Highlight Last Move"), HighlightLastMoveProc},
677     {N_("Move Sound"), MoveSoundProc},
678     {N_("ICS Alarm"), IcsAlarmProc},
679     {N_("Old Save Style"), OldSaveStyleProc},
680     {N_("Periodic Updates"), PeriodicUpdatesProc},
681     {N_("Ponder Next Move"), PonderNextMoveProc},
682     {N_("Popup Exit Message"), PopupExitMessageProc},
683     {N_("Popup Move Errors"), PopupMoveErrorsProc},
684     {N_("Premove"), PremoveProc},
685     {N_("Quiet Play"), QuietPlayProc},
686     {N_("Show Coords"), ShowCoordsProc},
687     {N_("Hide Thinking"), HideThinkingProc},
688     {N_("Test Legality"), TestLegalityProc},
689     {"----", NothingProc},
690     {N_("Save Settings Now"), SaveSettingsProc},
691     {N_("Save Settings on Exit"), SaveOnExitProc},
692     {NULL, NULL}
693 };
694
695 MenuItem helpMenu[] = {
696     {N_("Info XBoard"), InfoProc},
697     {N_("Man XBoard"), ManProc},
698     {"----", NothingProc},
699     {N_("Hint"), HintProc},
700     {N_("Book"), BookProc},
701     {"----", NothingProc},
702     {N_("About XBoard"), AboutProc},
703     {NULL, NULL}
704 };
705
706 Menu menuBar[] = {
707     {N_("File"), fileMenu},
708     {N_("Mode"), modeMenu},
709     {N_("Action"), actionMenu},
710     {N_("Step"), stepMenu},
711     {N_("Options"), optionsMenu},
712     {N_("Help"), helpMenu},
713     {NULL, NULL}
714 };
715
716 #define PAUSE_BUTTON N_("P")
717 MenuItem buttonBar[] = {
718     {"<<", ToStartProc},
719     {"<", BackwardProc},
720     {PAUSE_BUTTON, PauseProc},
721     {">", ForwardProc},
722     {">>", ToEndProc},
723     {NULL, NULL}
724 };
725
726 #define PIECE_MENU_SIZE 18
727 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
728     { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
729       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"), 
730       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"), 
731       N_("Empty square"), N_("Clear board") },
732     { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
733       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"), 
734       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"), 
735       N_("Empty square"), N_("Clear board") }
736 };
737 /* must be in same order as PieceMenuStrings! */
738 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
739     { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
740         WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
741         WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0, 
742         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
743     { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
744         BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
745         BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0, 
746         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
747 };
748
749 #define DROP_MENU_SIZE 6
750 String dropMenuStrings[DROP_MENU_SIZE] = {
751     "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
752   };
753 /* must be in same order as PieceMenuStrings! */
754 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
755     (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
756     WhiteRook, WhiteQueen
757 };
758
759 typedef struct {
760     char piece;
761     char* widget;
762 } DropMenuEnables;
763
764 DropMenuEnables dmEnables[] = {
765     { 'P', "Pawn" },
766     { 'N', "Knight" },
767     { 'B', "Bishop" },
768     { 'R', "Rook" },
769     { 'Q', "Queen" }
770 };
771
772 Arg shellArgs[] = {
773     { XtNwidth, 0 },
774     { XtNheight, 0 },
775     { XtNminWidth, 0 },
776     { XtNminHeight, 0 },
777     { XtNmaxWidth, 0 },
778     { XtNmaxHeight, 0 }
779 };
780
781 Arg layoutArgs[] = {
782     { XtNborderWidth, 0 },
783     { XtNdefaultDistance, 0 },
784 };
785
786 Arg formArgs[] = {
787     { XtNborderWidth, 0 },
788     { XtNresizable, (XtArgVal) True },
789 };
790
791 Arg boardArgs[] = {
792     { XtNborderWidth, 0 },
793     { XtNwidth, 0 },
794     { XtNheight, 0 }
795 };
796
797 Arg titleArgs[] = {
798     { XtNjustify, (XtArgVal) XtJustifyRight },
799     { XtNlabel, (XtArgVal) "..." },
800     { XtNresizable, (XtArgVal) True },
801     { XtNresize, (XtArgVal) False }
802 };
803
804 Arg messageArgs[] = {
805     { XtNjustify, (XtArgVal) XtJustifyLeft },
806     { XtNlabel, (XtArgVal) "..." },
807     { XtNresizable, (XtArgVal) True },
808     { XtNresize, (XtArgVal) False }
809 };
810
811 Arg timerArgs[] = {
812     { XtNborderWidth, 0 },
813     { XtNjustify, (XtArgVal) XtJustifyLeft }
814 };
815
816 XtResource clientResources[] = {
817     { "flashCount", "flashCount", XtRInt, sizeof(int),
818         XtOffset(AppDataPtr, flashCount), XtRImmediate,
819         (XtPointer) FLASH_COUNT  },
820 };
821
822 XrmOptionDescRec shellOptions[] = {
823     { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
824     { "-flash", "flashCount", XrmoptionNoArg, "3" },
825     { "-xflash", "flashCount", XrmoptionNoArg, "0" },
826 };
827
828 XtActionsRec boardActions[] = {
829     { "DrawPosition", DrawPositionProc },
830     { "HandleUserMove", HandleUserMove },
831     { "AnimateUserMove", AnimateUserMove },
832     { "FileNameAction", FileNameAction },
833     { "AskQuestionProc", AskQuestionProc },
834     { "AskQuestionReplyAction", AskQuestionReplyAction },
835     { "PieceMenuPopup", PieceMenuPopup },
836     { "WhiteClock", WhiteClock },
837     { "BlackClock", BlackClock },
838     { "Iconify", Iconify },
839     { "ResetProc", ResetProc },
840     { "LoadGameProc", LoadGameProc },
841     { "LoadNextGameProc", LoadNextGameProc },
842     { "LoadPrevGameProc", LoadPrevGameProc },
843     { "LoadSelectedProc", LoadSelectedProc },
844     { "ReloadGameProc", ReloadGameProc },
845     { "LoadPositionProc", LoadPositionProc },
846     { "LoadNextPositionProc", LoadNextPositionProc },
847     { "LoadPrevPositionProc", LoadPrevPositionProc },
848     { "ReloadPositionProc", ReloadPositionProc },
849     { "CopyPositionProc", CopyPositionProc },
850     { "PastePositionProc", PastePositionProc },
851     { "CopyGameProc", CopyGameProc },
852     { "PasteGameProc", PasteGameProc },
853     { "SaveGameProc", SaveGameProc },
854     { "SavePositionProc", SavePositionProc },
855     { "MailMoveProc", MailMoveProc },
856     { "ReloadCmailMsgProc", ReloadCmailMsgProc },
857     { "QuitProc", QuitProc },
858     { "MachineWhiteProc", MachineWhiteProc },
859     { "MachineBlackProc", MachineBlackProc },
860     { "AnalysisModeProc", AnalyzeModeProc },
861     { "AnalyzeFileProc", AnalyzeFileProc },
862     { "TwoMachinesProc", TwoMachinesProc },
863     { "IcsClientProc", IcsClientProc },
864     { "EditGameProc", EditGameProc },
865     { "EditPositionProc", EditPositionProc },
866     { "TrainingProc", EditPositionProc },
867     { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
868     { "ShowGameListProc", ShowGameListProc },
869     { "ShowMoveListProc", HistoryShowProc},
870     { "EditTagsProc", EditCommentProc },
871     { "EditCommentProc", EditCommentProc },
872     { "IcsAlarmProc", IcsAlarmProc },
873     { "IcsInputBoxProc", IcsInputBoxProc },
874     { "PauseProc", PauseProc },
875     { "AcceptProc", AcceptProc },
876     { "DeclineProc", DeclineProc },
877     { "RematchProc", RematchProc },
878     { "CallFlagProc", CallFlagProc },
879     { "DrawProc", DrawProc },
880     { "AdjournProc", AdjournProc },
881     { "AbortProc", AbortProc },
882     { "ResignProc", ResignProc },
883     { "AdjuWhiteProc", AdjuWhiteProc },
884     { "AdjuBlackProc", AdjuBlackProc },
885     { "AdjuDrawProc", AdjuDrawProc },
886     { "EnterKeyProc", EnterKeyProc },
887     { "StopObservingProc", StopObservingProc },
888     { "StopExaminingProc", StopExaminingProc },
889     { "BackwardProc", BackwardProc },
890     { "ForwardProc", ForwardProc },
891     { "ToStartProc", ToStartProc },
892     { "ToEndProc", ToEndProc },
893     { "RevertProc", RevertProc },
894     { "TruncateGameProc", TruncateGameProc },
895     { "MoveNowProc", MoveNowProc },
896     { "RetractMoveProc", RetractMoveProc },
897     { "AlwaysQueenProc", AlwaysQueenProc },
898     { "AnimateDraggingProc", AnimateDraggingProc },
899     { "AnimateMovingProc", AnimateMovingProc },
900     { "AutoflagProc", AutoflagProc },
901     { "AutoflipProc", AutoflipProc },
902     { "AutobsProc", AutobsProc },
903     { "AutoraiseProc", AutoraiseProc },
904     { "AutosaveProc", AutosaveProc },
905     { "BlindfoldProc", BlindfoldProc },
906     { "FlashMovesProc", FlashMovesProc },
907     { "FlipViewProc", FlipViewProc },
908     { "GetMoveListProc", GetMoveListProc },
909 #if HIGHDRAG
910     { "HighlightDraggingProc", HighlightDraggingProc },
911 #endif
912     { "HighlightLastMoveProc", HighlightLastMoveProc },
913     { "IcsAlarmProc", IcsAlarmProc },
914     { "MoveSoundProc", MoveSoundProc },
915     { "OldSaveStyleProc", OldSaveStyleProc },
916     { "PeriodicUpdatesProc", PeriodicUpdatesProc },
917     { "PonderNextMoveProc", PonderNextMoveProc },
918     { "PopupExitMessageProc", PopupExitMessageProc },
919     { "PopupMoveErrorsProc", PopupMoveErrorsProc },
920     { "PremoveProc", PremoveProc },
921     { "QuietPlayProc", QuietPlayProc },
922     { "ShowCoordsProc", ShowCoordsProc },
923     { "ShowThinkingProc", ShowThinkingProc },
924     { "HideThinkingProc", HideThinkingProc },
925     { "TestLegalityProc", TestLegalityProc },
926     { "SaveSettingsProc", SaveSettingsProc },
927     { "SaveOnExitProc", SaveOnExitProc },
928     { "InfoProc", InfoProc },
929     { "ManProc", ManProc },
930     { "HintProc", HintProc },
931     { "BookProc", BookProc },
932     { "AboutGameProc", AboutGameProc },
933     { "AboutProc", AboutProc },
934     { "DebugProc", DebugProc },
935     { "NothingProc", NothingProc },
936     { "CommentPopDown", (XtActionProc) CommentPopDown },
937     { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
938     { "TagsPopDown", (XtActionProc) TagsPopDown },
939     { "ErrorPopDown", (XtActionProc) ErrorPopDown },
940     { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
941     { "FileNamePopDown", (XtActionProc) FileNamePopDown },
942     { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
943     { "GameListPopDown", (XtActionProc) GameListPopDown },
944     { "PromotionPopDown", (XtActionProc) PromotionPopDown },
945     { "HistoryPopDown", (XtActionProc) HistoryPopDown },
946     { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
947     { "ShufflePopDown", (XtActionProc) ShufflePopDown },
948     { "EnginePopDown", (XtActionProc) EnginePopDown },
949     { "UciPopDown", (XtActionProc) UciPopDown },
950     { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
951     { "NewVariantPopDown", (XtActionProc) NewVariantPopDown },
952     { "SettingsPopDown", (XtActionProc) SettingsPopDown },
953 };
954
955 char globalTranslations[] =
956   ":<Key>R: ResignProc() \n \
957    :<Key>r: ResetProc() \n \
958    :<Key>g: LoadGameProc() \n \
959    :<Key>N: LoadNextGameProc() \n \
960    :<Key>P: LoadPrevGameProc() \n \
961    :<Key>Q: QuitProc() \n \
962    :<Key>F: ToEndProc() \n \
963    :<Key>f: ForwardProc() \n \
964    :<Key>B: ToStartProc() \n \
965    :<Key>b: BackwardProc() \n \
966    :<Key>p: PauseProc() \n \
967    :<Key>d: DrawProc() \n \
968    :<Key>t: CallFlagProc() \n \
969    :<Key>i: Iconify() \n \
970    :<Key>c: Iconify() \n \
971    :<Key>v: FlipViewProc() \n \
972    <KeyDown>Control_L: BackwardProc() \n \
973    <KeyUp>Control_L: ForwardProc() \n \
974    <KeyDown>Control_R: BackwardProc() \n \
975    <KeyUp>Control_R: ForwardProc() \n \
976    Shift<Key>1: AskQuestionProc(\"Direct command\",\
977                                 \"Send to chess program:\",,1) \n \
978    Shift<Key>2: AskQuestionProc(\"Direct command\",\
979                                 \"Send to second chess program:\",,2) \n";
980
981 char boardTranslations[] =
982    "<Btn1Down>: HandleUserMove() \n \
983    <Btn1Up>: HandleUserMove() \n \
984    <Btn1Motion>: AnimateUserMove() \n \
985    Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
986                  PieceMenuPopup(menuB) \n \
987    Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
988                  PieceMenuPopup(menuW) \n \
989    Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
990                  PieceMenuPopup(menuW) \n \
991    Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
992                  PieceMenuPopup(menuB) \n";
993
994 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
995 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
996
997 char ICSInputTranslations[] =
998     "<Key>Return: EnterKeyProc() \n";
999
1000 String xboardResources[] = {
1001     "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1002     "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1003     "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1004     NULL
1005   };
1006
1007
1008 /* Max possible square size */
1009 #define MAXSQSIZE 256
1010
1011 static int xpm_avail[MAXSQSIZE];
1012
1013 #ifdef HAVE_DIR_STRUCT
1014
1015 /* Extract piece size from filename */
1016 static int
1017 xpm_getsize(name, len, ext)
1018      char *name;
1019      int len;
1020      char *ext;
1021 {
1022     char *p, *d;
1023     char buf[10];
1024
1025     if (len < 4)
1026       return 0;
1027
1028     if ((p=strchr(name, '.')) == NULL ||
1029         StrCaseCmp(p+1, ext) != 0)
1030       return 0;
1031
1032     p = name + 3;
1033     d = buf;
1034
1035     while (*p && isdigit(*p))
1036       *(d++) = *(p++);
1037
1038     *d = 0;
1039     return atoi(buf);
1040 }
1041
1042 /* Setup xpm_avail */
1043 static int
1044 xpm_getavail(dirname, ext)
1045      char *dirname;
1046      char *ext;
1047 {
1048     DIR *dir;
1049     struct dirent *ent;
1050     int  i;
1051
1052     for (i=0; i<MAXSQSIZE; ++i)
1053       xpm_avail[i] = 0;
1054
1055     if (appData.debugMode)
1056       fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1057
1058     dir = opendir(dirname);
1059     if (!dir)
1060       {
1061           fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1062                   programName, dirname);
1063           exit(1);
1064       }
1065
1066     while ((ent=readdir(dir)) != NULL) {
1067         i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1068         if (i > 0 && i < MAXSQSIZE)
1069           xpm_avail[i] = 1;
1070     }
1071
1072     closedir(dir);
1073
1074     return 0;
1075 }
1076
1077 void
1078 xpm_print_avail(fp, ext)
1079      FILE *fp;
1080      char *ext;
1081 {
1082     int i;
1083
1084     fprintf(fp, _("Available `%s' sizes:\n"), ext);
1085     for (i=1; i<MAXSQSIZE; ++i) {
1086         if (xpm_avail[i])
1087           printf("%d\n", i);
1088     }
1089 }
1090
1091 /* Return XPM piecesize closest to size */
1092 int
1093 xpm_closest_to(dirname, size, ext)
1094      char *dirname;
1095      int size;
1096      char *ext;
1097 {
1098     int i;
1099     int sm_diff = MAXSQSIZE;
1100     int sm_index = 0;
1101     int diff;
1102
1103     xpm_getavail(dirname, ext);
1104
1105     if (appData.debugMode)
1106       xpm_print_avail(stderr, ext);
1107
1108     for (i=1; i<MAXSQSIZE; ++i) {
1109         if (xpm_avail[i]) {
1110             diff = size - i;
1111             diff = (diff<0) ? -diff : diff;
1112             if (diff < sm_diff) {
1113                 sm_diff = diff;
1114                 sm_index = i;
1115             }
1116         }
1117     }
1118
1119     if (!sm_index) {
1120         fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1121         exit(1);
1122     }
1123
1124     return sm_index;
1125 }
1126 #else   /* !HAVE_DIR_STRUCT */
1127 /* If we are on a system without a DIR struct, we can't
1128    read the directory, so we can't collect a list of
1129    filenames, etc., so we can't do any size-fitting. */
1130 int
1131 xpm_closest_to(dirname, size, ext)
1132      char *dirname;
1133      int size;
1134      char *ext;
1135 {
1136     fprintf(stderr, _("\
1137 Warning: No DIR structure found on this system --\n\
1138          Unable to autosize for XPM/XIM pieces.\n\
1139    Please report this error to frankm@hiwaay.net.\n\
1140    Include system type & operating system in message.\n"));
1141     return size;
1142 }
1143 #endif /* HAVE_DIR_STRUCT */
1144
1145 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1146                              "magenta", "cyan", "white" };
1147 typedef struct {
1148     int attr, bg, fg;
1149 } TextColors;
1150 TextColors textColors[(int)NColorClasses];
1151
1152 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1153 static int
1154 parse_color(str, which)
1155      char *str;
1156      int which;
1157 {
1158     char *p, buf[100], *d;
1159     int i;
1160
1161     if (strlen(str) > 99)       /* watch bounds on buf */
1162       return -1;
1163
1164     p = str;
1165     d = buf;
1166     for (i=0; i<which; ++i) {
1167         p = strchr(p, ',');
1168         if (!p)
1169           return -1;
1170         ++p;
1171     }
1172
1173     /* Could be looking at something like:
1174        black, , 1
1175        .. in which case we want to stop on a comma also */
1176     while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1177       ++p;
1178
1179     if (*p == ',') {
1180         return -1;              /* Use default for empty field */
1181     }
1182
1183     if (which == 2 || isdigit(*p))
1184       return atoi(p);
1185
1186     while (*p && isalpha(*p))
1187       *(d++) = *(p++);
1188
1189     *d = 0;
1190
1191     for (i=0; i<8; ++i) {
1192         if (!StrCaseCmp(buf, cnames[i]))
1193           return which? (i+40) : (i+30);
1194     }
1195     if (!StrCaseCmp(buf, "default")) return -1;
1196
1197     fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1198     return -2;
1199 }
1200
1201 static int
1202 parse_cpair(cc, str)
1203      ColorClass cc;
1204      char *str;
1205 {
1206     if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1207         fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1208                 programName, str);
1209         return -1;
1210     }
1211
1212     /* bg and attr are optional */
1213     textColors[(int)cc].bg = parse_color(str, 1);
1214     if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1215         textColors[(int)cc].attr = 0;
1216     }
1217     return 0;
1218 }
1219
1220
1221 /* Arrange to catch delete-window events */
1222 Atom wm_delete_window;
1223 void
1224 CatchDeleteWindow(Widget w, String procname)
1225 {
1226   char buf[MSG_SIZ];
1227   XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1228   snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1229   XtAugmentTranslations(w, XtParseTranslationTable(buf));
1230 }
1231
1232 void
1233 BoardToTop()
1234 {
1235   Arg args[16];
1236   XtSetArg(args[0], XtNiconic, False);
1237   XtSetValues(shellWidget, args, 1);
1238
1239   XtPopup(shellWidget, XtGrabNone); /* Raise if lowered  */
1240 }
1241
1242 //---------------------------------------------------------------------------------------------------------
1243 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1244 #define XBOARD True
1245 #define JAWS_ARGS
1246 #define CW_USEDEFAULT (1<<31)
1247 #define ICS_TEXT_MENU_SIZE 90
1248 #define SetCurrentDirectory chdir
1249 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1250 #define OPTCHAR "-"
1251 #define SEPCHAR " "
1252
1253 // these two must some day move to frontend.h, when they are implemented
1254 Boolean EvalGraphIsUp();
1255 Boolean MoveHistoryIsUp();
1256 Boolean GameListIsUp();
1257
1258 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1259 #include "args.h"
1260
1261 // front-end part of option handling
1262
1263 // [HGM] This platform-dependent table provides the location for storing the color info
1264 void *
1265 colorVariable[] = {
1266   &appData.whitePieceColor, 
1267   &appData.blackPieceColor, 
1268   &appData.lightSquareColor,
1269   &appData.darkSquareColor, 
1270   &appData.highlightSquareColor,
1271   &appData.premoveHighlightColor,
1272   NULL,
1273   NULL,
1274   NULL,
1275   NULL,
1276   NULL,
1277   NULL,
1278   NULL,
1279   NULL,
1280   NULL
1281 };
1282
1283 void
1284 ParseFont(char *name, int number)
1285 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1286   switch(number) {
1287     case 0: // CLOCK_FONT
1288         appData.clockFont = strdup(name);
1289       break;
1290     case 1: // MESSAGE_FONT
1291         appData.font = strdup(name);
1292       break;
1293     case 2: // COORD_FONT
1294         appData.coordFont = strdup(name);
1295       break;
1296     default:
1297       return;
1298   }
1299 }
1300
1301 void
1302 SetFontDefaults()
1303 { // only 2 fonts currently
1304   appData.clockFont = CLOCK_FONT_NAME;
1305   appData.coordFont = COORD_FONT_NAME;
1306   appData.font  =   DEFAULT_FONT_NAME;
1307 }
1308
1309 void
1310 CreateFonts()
1311 { // no-op, until we identify the code for this already in XBoard and move it here
1312 }
1313
1314 void
1315 ParseColor(int n, char *name)
1316 { // in XBoard, just copy the color-name string
1317   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1318 }
1319
1320 void
1321 ParseTextAttribs(ColorClass cc, char *s)
1322 {   
1323     (&appData.colorShout)[cc] = strdup(s);
1324 }
1325
1326 void
1327 ParseBoardSize(void *addr, char *name)
1328 {
1329     appData.boardSize = strdup(name);
1330 }
1331
1332 void
1333 LoadAllSounds()
1334 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1335 }
1336
1337 void
1338 SetCommPortDefaults()
1339 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1340 }
1341
1342 // [HGM] args: these three cases taken out to stay in front-end
1343 void
1344 SaveFontArg(FILE *f, ArgDescriptor *ad)
1345 {
1346   char *name;
1347   switch((int)ad->argLoc) {
1348     case 0: // CLOCK_FONT
1349         name = appData.clockFont;
1350       break;
1351     case 1: // MESSAGE_FONT
1352         name = appData.font;
1353       break;
1354     case 2: // COORD_FONT
1355         name = appData.coordFont;
1356       break;
1357     default:
1358       return;
1359   }
1360   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, name);
1361 }
1362
1363 void
1364 ExportSounds()
1365 { // nothing to do, as the sounds are at all times represented by their text-string names already
1366 }
1367
1368 void
1369 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1370 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1371         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
1372 }
1373
1374 void
1375 SaveColor(FILE *f, ArgDescriptor *ad)
1376 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1377         if(colorVariable[(int)ad->argLoc])
1378         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
1379 }
1380
1381 void
1382 SaveBoardSize(FILE *f, char *name, void *addr)
1383 { // wrapper to shield back-end from BoardSize & sizeInfo
1384   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1385 }
1386
1387 void
1388 ParseCommPortSettings(char *s)
1389 { // no such option in XBoard (yet)
1390 }
1391
1392 extern Widget engineOutputShell;
1393 extern Widget tagsShell, editTagsShell;
1394 void
1395 GetActualPlacement(Widget wg, WindowPlacement *wp)
1396 {
1397   Arg args[16];
1398   Dimension w, h;
1399   Position x, y;
1400   int i;
1401
1402   if(!wg) return;
1403   
1404     i = 0;
1405     XtSetArg(args[i], XtNx, &x); i++;
1406     XtSetArg(args[i], XtNy, &y); i++;
1407     XtSetArg(args[i], XtNwidth, &w); i++;
1408     XtSetArg(args[i], XtNheight, &h); i++;
1409     XtGetValues(wg, args, i);
1410     wp->x = x - 4;
1411     wp->y = y - 23;
1412     wp->height = h;
1413     wp->width = w;
1414 }
1415
1416 void
1417 GetWindowCoords()
1418 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1419   // In XBoard this will have to wait until awareness of window parameters is implemented
1420   GetActualPlacement(shellWidget, &wpMain);
1421   if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1422   if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1423   if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1424   if(commentShell) GetActualPlacement(commentShell, &wpComment);
1425   else             GetActualPlacement(editShell,    &wpComment);
1426   if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1427   else      GetActualPlacement(editTagsShell, &wpTags);
1428 #if 0
1429   GetActualPlacement(hwndConsole, &wpConsole);
1430   GetActualPlacement(evalGraphDialog, &wpEvalGraph);
1431 #endif
1432 }
1433
1434 void
1435 PrintCommPortSettings(FILE *f, char *name)
1436 { // This option does not exist in XBoard
1437 }
1438
1439 int
1440 MySearchPath(char *installDir, char *name, char *fullname)
1441 { // just append installDir and name. Perhaps ExpandPath should be used here?
1442   name = ExpandPathName(name);
1443   if(name && name[0] == '/') strcpy(fullname, name); else {
1444     sprintf(fullname, "%s%c%s", installDir, '/', name);
1445   }
1446   return 1;
1447 }
1448
1449 int
1450 MyGetFullPathName(char *name, char *fullname)
1451 { // should use ExpandPath?
1452   name = ExpandPathName(name);
1453   strcpy(fullname, name);
1454   return 1;
1455 }
1456
1457 void
1458 EnsureOnScreen(int *x, int *y, int minX, int minY)
1459 {
1460   return;
1461 }
1462
1463 Boolean
1464 EvalGraphIsUp()
1465 {
1466   return False;
1467 }
1468
1469 int
1470 MainWindowUp()
1471 { // [HGM] args: allows testing if main window is realized from back-end
1472   return xBoardWindow != 0;
1473 }
1474
1475 void
1476 PopUpStartupDialog()
1477 {  // start menu not implemented in XBoard
1478 }
1479 char *
1480 ConvertToLine(int argc, char **argv)
1481 {
1482   static char line[128*1024], buf[1024];
1483   int i;
1484
1485   line[0] = NULLCHAR;
1486   for(i=1; i<argc; i++) {
1487     if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1488         && argv[i][0] != '{' )
1489          sprintf(buf, "{%s} ", argv[i]);
1490     else sprintf(buf, "%s ", argv[i]);
1491     strcat(line, buf);
1492   }
1493     line[strlen(line)-1] = NULLCHAR;
1494   return line;
1495 }
1496
1497 //--------------------------------------------------------------------------------------------
1498
1499 #ifdef IDSIZES
1500   // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1501 #else
1502 #define BoardSize int
1503 void InitDrawingSizes(BoardSize boardSize, int flags)
1504 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1505     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1506     Arg args[16];
1507     XtGeometryResult gres;
1508     int i;
1509
1510     if(!formWidget) return;
1511
1512     /*
1513      * Enable shell resizing.
1514      */
1515     shellArgs[0].value = (XtArgVal) &w;
1516     shellArgs[1].value = (XtArgVal) &h;
1517     XtGetValues(shellWidget, shellArgs, 2);
1518
1519     shellArgs[4].value = 2*w; shellArgs[2].value = 10;
1520     shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1521     XtSetValues(shellWidget, &shellArgs[2], 4);
1522
1523     XtSetArg(args[0], XtNdefaultDistance, &sep);
1524     XtGetValues(formWidget, args, 1);
1525
1526     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1527     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1528     CreateGrid();
1529
1530     XtSetArg(args[0], XtNwidth, boardWidth);
1531     XtSetArg(args[1], XtNheight, boardHeight);
1532     XtSetValues(boardWidget, args, 2);
1533
1534     timerWidth = (boardWidth - sep) / 2;
1535     XtSetArg(args[0], XtNwidth, timerWidth);
1536     XtSetValues(whiteTimerWidget, args, 1);
1537     XtSetValues(blackTimerWidget, args, 1);
1538
1539     XawFormDoLayout(formWidget, False);
1540
1541     if (appData.titleInWindow) {
1542         i = 0;
1543         XtSetArg(args[i], XtNborderWidth, &bor); i++;
1544         XtSetArg(args[i], XtNheight, &h);  i++;
1545         XtGetValues(titleWidget, args, i);
1546         if (smallLayout) {
1547             w = boardWidth - 2*bor;
1548         } else {
1549             XtSetArg(args[0], XtNwidth, &w);
1550             XtGetValues(menuBarWidget, args, 1);
1551             w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1552         }
1553
1554         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1555         if (gres != XtGeometryYes && appData.debugMode) {
1556             fprintf(stderr,
1557                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1558                     programName, gres, w, h, wr, hr);
1559         }
1560     }
1561
1562     XawFormDoLayout(formWidget, True);
1563
1564     /*
1565      * Inhibit shell resizing.
1566      */
1567     shellArgs[0].value = w = (XtArgVal) boardWidth + marginW;
1568     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1569     shellArgs[4].value = shellArgs[2].value = w;
1570     shellArgs[5].value = shellArgs[3].value = h;
1571     XtSetValues(shellWidget, &shellArgs[0], 6);
1572
1573     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1574     // (only for xpm)
1575     if(useImages) {
1576       for(i=0; i<4; i++) {
1577         int p;
1578         for(p=0; p<=(int)WhiteKing; p++)
1579            xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1580         if(gameInfo.variant == VariantShogi) {
1581            xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1582            xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1583            xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1584            xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1585            xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1586         }
1587 #ifdef GOTHIC
1588         if(gameInfo.variant == VariantGothic) {
1589            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1590         }
1591 #endif
1592 #if !HAVE_LIBXPM
1593         // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1594         for(p=0; p<=(int)WhiteKing; p++)
1595            ximMaskPm[p] = ximMaskPm2[p]; // defaults
1596         if(gameInfo.variant == VariantShogi) {
1597            ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1598            ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1599            ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1600            ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1601            ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1602         }
1603 #ifdef GOTHIC
1604         if(gameInfo.variant == VariantGothic) {
1605            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1606         }
1607 #endif
1608 #endif
1609       }
1610     } else {
1611       for(i=0; i<2; i++) {
1612         int p;
1613         for(p=0; p<=(int)WhiteKing; p++)
1614            pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1615         if(gameInfo.variant == VariantShogi) {
1616            pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1617            pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1618            pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1619            pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1620            pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1621         }
1622 #ifdef GOTHIC
1623         if(gameInfo.variant == VariantGothic) {
1624            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1625         }
1626 #endif
1627       }
1628     }
1629 #if HAVE_LIBXPM
1630     CreateAnimVars();
1631 #endif
1632 }
1633 #endif
1634
1635 void EscapeExpand(char *p, char *q)
1636 {       // [HGM] initstring: routine to shape up string arguments
1637         while(*p++ = *q++) if(p[-1] == '\\')
1638             switch(*q++) {
1639                 case 'n': p[-1] = '\n'; break;
1640                 case 'r': p[-1] = '\r'; break;
1641                 case 't': p[-1] = '\t'; break;
1642                 case '\\': p[-1] = '\\'; break;
1643                 case 0: *p = 0; return;
1644                 default: p[-1] = q[-1]; break;
1645             }
1646 }
1647
1648 int
1649 main(argc, argv)
1650      int argc;
1651      char **argv;
1652 {
1653     int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1654     XSetWindowAttributes window_attributes;
1655     Arg args[16];
1656     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1657     XrmValue vFrom, vTo;
1658     XtGeometryResult gres;
1659     char *p;
1660     XrmDatabase xdb;
1661     int forceMono = False;
1662 //define INDIRECTION
1663 #ifdef INDIRECTION
1664     // [HGM] before anything else, expand any indirection files amongst options
1665     char *argvCopy[1000]; // 1000 seems enough
1666     char newArgs[10000];  // holds actual characters
1667     int k = 0;
1668
1669     srandom(time(0)); // [HGM] book: make random truly random
1670
1671     j = 0;
1672     for(i=0; i<argc; i++) {
1673         if(j >= 1000-2) { printf(_("too many arguments\n")); exit(-1); }
1674 //fprintf(stderr, "arg %s\n", argv[i]);
1675         if(argv[i][0] != '@') argvCopy[j++] = argv[i]; else {
1676             char c;
1677             FILE *f = fopen(argv[i]+1, "rb");
1678             if(f == NULL) { fprintf(stderr, _("ignore %s\n"), argv[i]); continue; } // do not expand non-existing
1679             argvCopy[j++] = newArgs + k; // get ready for first argument from file
1680             while((c = fgetc(f)) != EOF) { // each line of file inserts 1 argument in the list
1681                 if(c == '\n') {
1682                     if(j >= 1000-2) { printf(_("too many arguments\n")); exit(-1); }
1683                     newArgs[k++] = 0;  // terminate current arg
1684                     if(k >= 10000-1) { printf(_("too long arguments\n")); exit(-1); }
1685                     argvCopy[j++] = newArgs + k; // get ready for next
1686                 } else {
1687                     if(k >= 10000-1) { printf(_("too long arguments\n")); exit(-1); }
1688                     newArgs[k++] = c;
1689                 }
1690             }
1691             newArgs[k] = 0;
1692             j--;
1693             fclose(f);
1694         }
1695     }
1696     argvCopy[j] = NULL;
1697     argv = argvCopy;
1698     argc = j;
1699 #endif
1700
1701     setbuf(stdout, NULL);
1702     setbuf(stderr, NULL);
1703     debugFP = stderr;
1704
1705     programName = strrchr(argv[0], '/');
1706     if (programName == NULL)
1707       programName = argv[0];
1708     else
1709       programName++;
1710
1711 #ifdef ENABLE_NLS
1712     XtSetLanguageProc(NULL, NULL, NULL);
1713     bindtextdomain(PACKAGE, LOCALEDIR);
1714     textdomain(PACKAGE);
1715 #endif
1716
1717     shellWidget =
1718       XtAppInitialize(&appContext, "XBoard", shellOptions,
1719                       XtNumber(shellOptions),
1720                       &argc, argv, xboardResources, NULL, 0);
1721     appData.boardSize = "";
1722     InitAppData(ConvertToLine(argc, argv));
1723     p = getenv("HOME");
1724     if (p == NULL) p = "/tmp";
1725     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1726     gameCopyFilename = (char*) malloc(i);
1727     gamePasteFilename = (char*) malloc(i);
1728     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1729     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1730
1731     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1732                               clientResources, XtNumber(clientResources),
1733                               NULL, 0);
1734
1735     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1736         static char buf[MSG_SIZ];
1737         EscapeExpand(buf, appData.initString);
1738         appData.initString = strdup(buf);
1739         EscapeExpand(buf, appData.secondInitString);
1740         appData.secondInitString = strdup(buf);
1741         EscapeExpand(buf, appData.firstComputerString);
1742         appData.firstComputerString = strdup(buf);
1743         EscapeExpand(buf, appData.secondComputerString);
1744         appData.secondComputerString = strdup(buf);
1745     }
1746
1747     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1748         chessDir = ".";
1749     } else {
1750         if (chdir(chessDir) != 0) {
1751             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1752             perror(chessDir);
1753             exit(1);
1754         }
1755     }
1756
1757     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1758         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1759         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
1760            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1761            exit(errno);
1762         }
1763         setbuf(debugFP, NULL);
1764     }
1765
1766     /* [HGM,HR] make sure board size is acceptable */
1767     if(appData.NrFiles > BOARD_FILES ||
1768        appData.NrRanks > BOARD_RANKS   )
1769          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1770
1771 #if !HIGHDRAG
1772     /* This feature does not work; animation needs a rewrite */
1773     appData.highlightDragging = FALSE;
1774 #endif
1775     InitBackEnd1();
1776
1777     xDisplay = XtDisplay(shellWidget);
1778     xScreen = DefaultScreen(xDisplay);
1779     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1780
1781         gameInfo.variant = StringToVariant(appData.variant);
1782         InitPosition(FALSE);
1783
1784 #ifdef IDSIZE
1785     InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1786 #else
1787     if (isdigit(appData.boardSize[0])) {
1788         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1789                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1790                    &fontPxlSize, &smallLayout, &tinyLayout);
1791         if (i == 0) {
1792             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1793                     programName, appData.boardSize);
1794             exit(2);
1795         }
1796         if (i < 7) {
1797             /* Find some defaults; use the nearest known size */
1798             SizeDefaults *szd, *nearest;
1799             int distance = 99999;
1800             nearest = szd = sizeDefaults;
1801             while (szd->name != NULL) {
1802                 if (abs(szd->squareSize - squareSize) < distance) {
1803                     nearest = szd;
1804                     distance = abs(szd->squareSize - squareSize);
1805                     if (distance == 0) break;
1806                 }
1807                 szd++;
1808             }
1809             if (i < 2) lineGap = nearest->lineGap;
1810             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1811             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1812             if (i < 5) fontPxlSize = nearest->fontPxlSize;
1813             if (i < 6) smallLayout = nearest->smallLayout;
1814             if (i < 7) tinyLayout = nearest->tinyLayout;
1815         }
1816     } else {
1817         SizeDefaults *szd = sizeDefaults;
1818         if (*appData.boardSize == NULLCHAR) {
1819             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1820                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1821               szd++;
1822             }
1823             if (szd->name == NULL) szd--;
1824             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1825         } else {
1826             while (szd->name != NULL &&
1827                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1828             if (szd->name == NULL) {
1829                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1830                         programName, appData.boardSize);
1831                 exit(2);
1832             }
1833         }
1834         squareSize = szd->squareSize;
1835         lineGap = szd->lineGap;
1836         clockFontPxlSize = szd->clockFontPxlSize;
1837         coordFontPxlSize = szd->coordFontPxlSize;
1838         fontPxlSize = szd->fontPxlSize;
1839         smallLayout = szd->smallLayout;
1840         tinyLayout = szd->tinyLayout;
1841     }
1842
1843     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1844     if (strlen(appData.pixmapDirectory) > 0) {
1845         p = ExpandPathName(appData.pixmapDirectory);
1846         if (!p) {
1847             fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1848                    appData.pixmapDirectory);
1849             exit(1);
1850         }
1851         if (appData.debugMode) {
1852           fprintf(stderr, _("\
1853 XBoard square size (hint): %d\n\
1854 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1855         }
1856         squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1857         if (appData.debugMode) {
1858             fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1859         }
1860     }
1861
1862     /* [HR] height treated separately (hacked) */
1863     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1864     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1865     if (appData.showJail == 1) {
1866         /* Jail on top and bottom */
1867         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1868         XtSetArg(boardArgs[2], XtNheight,
1869                  boardHeight + 2*(lineGap + squareSize));
1870     } else if (appData.showJail == 2) {
1871         /* Jail on sides */
1872         XtSetArg(boardArgs[1], XtNwidth,
1873                  boardWidth + 2*(lineGap + squareSize));
1874         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1875     } else {
1876         /* No jail */
1877         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1878         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1879     }
1880
1881     /*
1882      * Determine what fonts to use.
1883      */
1884     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1885     clockFontID = XLoadFont(xDisplay, appData.clockFont);
1886     clockFontStruct = XQueryFont(xDisplay, clockFontID);
1887     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1888     coordFontID = XLoadFont(xDisplay, appData.coordFont);
1889     coordFontStruct = XQueryFont(xDisplay, coordFontID);
1890     appData.font = FindFont(appData.font, fontPxlSize);
1891     countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
1892     countFontStruct = XQueryFont(xDisplay, countFontID);
1893 //    appData.font = FindFont(appData.font, fontPxlSize);
1894
1895     xdb = XtDatabase(xDisplay);
1896     XrmPutStringResource(&xdb, "*font", appData.font);
1897
1898     /*
1899      * Detect if there are not enough colors available and adapt.
1900      */
1901     if (DefaultDepth(xDisplay, xScreen) <= 2) {
1902       appData.monoMode = True;
1903     }
1904
1905     if (!appData.monoMode) {
1906         vFrom.addr = (caddr_t) appData.lightSquareColor;
1907         vFrom.size = strlen(appData.lightSquareColor);
1908         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1909         if (vTo.addr == NULL) {
1910           appData.monoMode = True;
1911           forceMono = True;
1912         } else {
1913           lightSquareColor = *(Pixel *) vTo.addr;
1914         }
1915     }
1916     if (!appData.monoMode) {
1917         vFrom.addr = (caddr_t) appData.darkSquareColor;
1918         vFrom.size = strlen(appData.darkSquareColor);
1919         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1920         if (vTo.addr == NULL) {
1921           appData.monoMode = True;
1922           forceMono = True;
1923         } else {
1924           darkSquareColor = *(Pixel *) vTo.addr;
1925         }
1926     }
1927     if (!appData.monoMode) {
1928         vFrom.addr = (caddr_t) appData.whitePieceColor;
1929         vFrom.size = strlen(appData.whitePieceColor);
1930         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1931         if (vTo.addr == NULL) {
1932           appData.monoMode = True;
1933           forceMono = True;
1934         } else {
1935           whitePieceColor = *(Pixel *) vTo.addr;
1936         }
1937     }
1938     if (!appData.monoMode) {
1939         vFrom.addr = (caddr_t) appData.blackPieceColor;
1940         vFrom.size = strlen(appData.blackPieceColor);
1941         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1942         if (vTo.addr == NULL) {
1943           appData.monoMode = True;
1944           forceMono = True;
1945         } else {
1946           blackPieceColor = *(Pixel *) vTo.addr;
1947         }
1948     }
1949
1950     if (!appData.monoMode) {
1951         vFrom.addr = (caddr_t) appData.highlightSquareColor;
1952         vFrom.size = strlen(appData.highlightSquareColor);
1953         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1954         if (vTo.addr == NULL) {
1955           appData.monoMode = True;
1956           forceMono = True;
1957         } else {
1958           highlightSquareColor = *(Pixel *) vTo.addr;
1959         }
1960     }
1961
1962     if (!appData.monoMode) {
1963         vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1964         vFrom.size = strlen(appData.premoveHighlightColor);
1965         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1966         if (vTo.addr == NULL) {
1967           appData.monoMode = True;
1968           forceMono = True;
1969         } else {
1970           premoveHighlightColor = *(Pixel *) vTo.addr;
1971         }
1972     }
1973
1974     if (forceMono) {
1975       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1976               programName);
1977       
1978       if (appData.bitmapDirectory == NULL ||
1979               appData.bitmapDirectory[0] == NULLCHAR)
1980             appData.bitmapDirectory = DEF_BITMAP_DIR;
1981     }
1982
1983     if (appData.lowTimeWarning && !appData.monoMode) {
1984       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1985       vFrom.size = strlen(appData.lowTimeWarningColor);
1986       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1987       if (vTo.addr == NULL) 
1988                 appData.monoMode = True;
1989       else
1990                 lowTimeWarningColor = *(Pixel *) vTo.addr;
1991     }
1992
1993     if (appData.monoMode && appData.debugMode) {
1994         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1995                 (unsigned long) XWhitePixel(xDisplay, xScreen),
1996                 (unsigned long) XBlackPixel(xDisplay, xScreen));
1997     }
1998
1999     if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
2000         parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
2001         parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
2002         parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
2003         parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
2004         parse_cpair(ColorTell, appData.colorTell) < 0 ||
2005         parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
2006         parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
2007         parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
2008         parse_cpair(ColorNormal, appData.colorNormal) < 0)
2009       {
2010           if (appData.colorize) {
2011               fprintf(stderr,
2012                       _("%s: can't parse color names; disabling colorization\n"),
2013                       programName);
2014           }
2015           appData.colorize = FALSE;
2016       }
2017     textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2018     textColors[ColorNone].attr = 0;
2019
2020     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2021
2022     /*
2023      * widget hierarchy
2024      */
2025     if (tinyLayout) {
2026         layoutName = "tinyLayout";
2027     } else if (smallLayout) {
2028         layoutName = "smallLayout";
2029     } else {
2030         layoutName = "normalLayout";
2031     }
2032     /* Outer layoutWidget is there only to provide a name for use in
2033        resources that depend on the layout style */
2034     layoutWidget =
2035       XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2036                             layoutArgs, XtNumber(layoutArgs));
2037     formWidget =
2038       XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2039                             formArgs, XtNumber(formArgs));
2040     XtSetArg(args[0], XtNdefaultDistance, &sep);
2041     XtGetValues(formWidget, args, 1);
2042
2043     j = 0;
2044     widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2045     XtSetArg(args[0], XtNtop,    XtChainTop);
2046     XtSetArg(args[1], XtNbottom, XtChainTop);
2047     XtSetArg(args[2], XtNright,  XtChainLeft);
2048     XtSetValues(menuBarWidget, args, 3);
2049
2050     widgetList[j++] = whiteTimerWidget =
2051       XtCreateWidget("whiteTime", labelWidgetClass,
2052                      formWidget, timerArgs, XtNumber(timerArgs));
2053     XtSetArg(args[0], XtNfont, clockFontStruct);
2054     XtSetArg(args[1], XtNtop,    XtChainTop);
2055     XtSetArg(args[2], XtNbottom, XtChainTop);
2056     XtSetValues(whiteTimerWidget, args, 3);
2057
2058     widgetList[j++] = blackTimerWidget =
2059       XtCreateWidget("blackTime", labelWidgetClass,
2060                      formWidget, timerArgs, XtNumber(timerArgs));
2061     XtSetArg(args[0], XtNfont, clockFontStruct);
2062     XtSetArg(args[1], XtNtop,    XtChainTop);
2063     XtSetArg(args[2], XtNbottom, XtChainTop);
2064     XtSetValues(blackTimerWidget, args, 3);
2065
2066     if (appData.titleInWindow) {
2067         widgetList[j++] = titleWidget =
2068           XtCreateWidget("title", labelWidgetClass, formWidget,
2069                          titleArgs, XtNumber(titleArgs));
2070         XtSetArg(args[0], XtNtop,    XtChainTop);
2071         XtSetArg(args[1], XtNbottom, XtChainTop);
2072         XtSetValues(titleWidget, args, 2);
2073     }
2074
2075     if (appData.showButtonBar) {
2076       widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2077       XtSetArg(args[0], XtNleft,  XtChainRight); // [HGM] glue to right window edge
2078       XtSetArg(args[1], XtNright, XtChainRight); //       for good run-time sizing
2079       XtSetArg(args[2], XtNtop,    XtChainTop);
2080       XtSetArg(args[3], XtNbottom, XtChainTop);
2081       XtSetValues(buttonBarWidget, args, 4);
2082     }
2083
2084     widgetList[j++] = messageWidget =
2085       XtCreateWidget("message", labelWidgetClass, formWidget,
2086                      messageArgs, XtNumber(messageArgs));
2087     XtSetArg(args[0], XtNtop,    XtChainTop);
2088     XtSetArg(args[1], XtNbottom, XtChainTop);
2089     XtSetValues(messageWidget, args, 2);
2090
2091     widgetList[j++] = boardWidget =
2092       XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2093                      XtNumber(boardArgs));
2094
2095     XtManageChildren(widgetList, j);
2096
2097     timerWidth = (boardWidth - sep) / 2;
2098     XtSetArg(args[0], XtNwidth, timerWidth);
2099     XtSetValues(whiteTimerWidget, args, 1);
2100     XtSetValues(blackTimerWidget, args, 1);
2101
2102     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2103     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2104     XtGetValues(whiteTimerWidget, args, 2);
2105
2106     if (appData.showButtonBar) {
2107       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2108       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2109       XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2110     }
2111
2112     /*
2113      * formWidget uses these constraints but they are stored
2114      * in the children.
2115      */
2116     i = 0;
2117     XtSetArg(args[i], XtNfromHoriz, 0); i++;
2118     XtSetValues(menuBarWidget, args, i);
2119     if (appData.titleInWindow) {
2120         if (smallLayout) {
2121             i = 0;
2122             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2123             XtSetValues(whiteTimerWidget, args, i);
2124             i = 0;
2125             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2126             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2127             XtSetValues(blackTimerWidget, args, i);
2128             i = 0;
2129             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2130             XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2131             XtSetValues(titleWidget, args, i);
2132             i = 0;
2133             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2134             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2135             XtSetValues(messageWidget, args, i);
2136             if (appData.showButtonBar) {
2137               i = 0;
2138               XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2139               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2140               XtSetValues(buttonBarWidget, args, i);
2141             }
2142         } else {
2143             i = 0;
2144             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2145             XtSetValues(whiteTimerWidget, args, i);
2146             i = 0;
2147             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2148             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2149             XtSetValues(blackTimerWidget, args, i);
2150             i = 0;
2151             XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2152             XtSetValues(titleWidget, args, i);
2153             i = 0;
2154             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2155             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2156             XtSetValues(messageWidget, args, i);
2157             if (appData.showButtonBar) {
2158               i = 0;
2159               XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2160               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2161               XtSetValues(buttonBarWidget, args, i);
2162             }
2163         }
2164     } else {
2165         i = 0;
2166         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2167         XtSetValues(whiteTimerWidget, args, i);
2168         i = 0;
2169         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2170         XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2171         XtSetValues(blackTimerWidget, args, i);
2172         i = 0;
2173         XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2174         XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2175         XtSetValues(messageWidget, args, i);
2176         if (appData.showButtonBar) {
2177           i = 0;
2178           XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2179           XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2180           XtSetValues(buttonBarWidget, args, i);
2181         }
2182     }
2183     i = 0;
2184     XtSetArg(args[0], XtNfromVert, messageWidget);
2185     XtSetArg(args[1], XtNtop,    XtChainTop);
2186     XtSetArg(args[2], XtNbottom, XtChainBottom);
2187     XtSetArg(args[3], XtNleft,   XtChainLeft);
2188     XtSetArg(args[4], XtNright,  XtChainRight);
2189     XtSetValues(boardWidget, args, 5);
2190
2191     XtRealizeWidget(shellWidget);
2192
2193     if(wpMain.x > 0) {
2194       XtSetArg(args[0], XtNx, wpMain.x);
2195       XtSetArg(args[1], XtNy, wpMain.y);
2196       XtSetValues(shellWidget, args, 2);
2197     }
2198
2199     /*
2200      * Correct the width of the message and title widgets.
2201      * It is not known why some systems need the extra fudge term.
2202      * The value "2" is probably larger than needed.
2203      */
2204     XawFormDoLayout(formWidget, False);
2205
2206 #define WIDTH_FUDGE 2
2207     i = 0;
2208     XtSetArg(args[i], XtNborderWidth, &bor);  i++;
2209     XtSetArg(args[i], XtNheight, &h);  i++;
2210     XtGetValues(messageWidget, args, i);
2211     if (appData.showButtonBar) {
2212       i = 0;
2213       XtSetArg(args[i], XtNwidth, &w);  i++;
2214       XtGetValues(buttonBarWidget, args, i);
2215       w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2216     } else {
2217       w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2218     }
2219
2220     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2221     if (gres != XtGeometryYes && appData.debugMode) {
2222       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2223               programName, gres, w, h, wr, hr);
2224     }
2225
2226     /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2227     /* The size used for the child widget in layout lags one resize behind
2228        its true size, so we resize a second time, 1 pixel smaller.  Yeech! */
2229     w--;
2230     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2231     if (gres != XtGeometryYes && appData.debugMode) {
2232       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2233               programName, gres, w, h, wr, hr);
2234     }
2235     /* !! end hack */
2236     XtSetArg(args[0], XtNleft,  XtChainLeft);  // [HGM] glue ends for good run-time sizing
2237     XtSetArg(args[1], XtNright, XtChainRight);
2238     XtSetValues(messageWidget, args, 2);
2239
2240     if (appData.titleInWindow) {
2241         i = 0;
2242         XtSetArg(args[i], XtNborderWidth, &bor); i++;
2243         XtSetArg(args[i], XtNheight, &h);  i++;
2244         XtGetValues(titleWidget, args, i);
2245         if (smallLayout) {
2246             w = boardWidth - 2*bor;
2247         } else {
2248             XtSetArg(args[0], XtNwidth, &w);
2249             XtGetValues(menuBarWidget, args, 1);
2250             w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2251         }
2252
2253         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2254         if (gres != XtGeometryYes && appData.debugMode) {
2255             fprintf(stderr,
2256                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2257                     programName, gres, w, h, wr, hr);
2258         }
2259     }
2260     XawFormDoLayout(formWidget, True);
2261
2262     xBoardWindow = XtWindow(boardWidget);
2263
2264     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2265     //       not need to go into InitDrawingSizes().
2266 #endif
2267
2268     /*
2269      * Create X checkmark bitmap and initialize option menu checks.
2270      */
2271     ReadBitmap(&xMarkPixmap, "checkmark.bm",
2272                checkmark_bits, checkmark_width, checkmark_height);
2273     XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2274     if (appData.alwaysPromoteToQueen) {
2275         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2276                     args, 1);
2277     }
2278     if (appData.animateDragging) {
2279         XtSetValues(XtNameToWidget(menuBarWidget,
2280                                    "menuOptions.Animate Dragging"),
2281                     args, 1);
2282     }
2283     if (appData.animate) {
2284         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2285                     args, 1);
2286     }
2287     if (appData.autoComment) {
2288         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
2289                     args, 1);
2290     }
2291     if (appData.autoCallFlag) {
2292         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2293                     args, 1);
2294     }
2295     if (appData.autoFlipView) {
2296         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2297                     args, 1);
2298     }
2299     if (appData.autoObserve) {
2300         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
2301                     args, 1);
2302     }
2303     if (appData.autoRaiseBoard) {
2304         XtSetValues(XtNameToWidget(menuBarWidget,
2305                                    "menuOptions.Auto Raise Board"), args, 1);
2306     }
2307     if (appData.autoSaveGames) {
2308         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2309                     args, 1);
2310     }
2311     if (appData.saveGameFile[0] != NULLCHAR) {
2312         /* Can't turn this off from menu */
2313         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2314                     args, 1);
2315         XtSetSensitive(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2316                        False);
2317
2318     }
2319     if (appData.blindfold) {
2320         XtSetValues(XtNameToWidget(menuBarWidget,
2321                                    "menuOptions.Blindfold"), args, 1);
2322     }
2323     if (appData.flashCount > 0) {
2324         XtSetValues(XtNameToWidget(menuBarWidget,
2325                                    "menuOptions.Flash Moves"),
2326                     args, 1);
2327     }
2328     if (appData.getMoveList) {
2329         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
2330                     args, 1);
2331     }
2332 #if HIGHDRAG
2333     if (appData.highlightDragging) {
2334         XtSetValues(XtNameToWidget(menuBarWidget,
2335                                    "menuOptions.Highlight Dragging"),
2336                     args, 1);
2337     }
2338 #endif
2339     if (appData.highlightLastMove) {
2340         XtSetValues(XtNameToWidget(menuBarWidget,
2341                                    "menuOptions.Highlight Last Move"),
2342                     args, 1);
2343     }
2344     if (appData.icsAlarm) {
2345         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2346                     args, 1);
2347     }
2348     if (appData.ringBellAfterMoves) {
2349         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2350                     args, 1);
2351     }
2352     if (appData.oldSaveStyle) {
2353         XtSetValues(XtNameToWidget(menuBarWidget,
2354                                    "menuOptions.Old Save Style"), args, 1);
2355     }
2356     if (appData.periodicUpdates) {
2357         XtSetValues(XtNameToWidget(menuBarWidget,
2358                                    "menuOptions.Periodic Updates"), args, 1);
2359     }
2360     if (appData.ponderNextMove) {
2361         XtSetValues(XtNameToWidget(menuBarWidget,
2362                                    "menuOptions.Ponder Next Move"), args, 1);
2363     }
2364     if (appData.popupExitMessage) {
2365         XtSetValues(XtNameToWidget(menuBarWidget,
2366                                    "menuOptions.Popup Exit Message"), args, 1);
2367     }
2368     if (appData.popupMoveErrors) {
2369         XtSetValues(XtNameToWidget(menuBarWidget,
2370                                    "menuOptions.Popup Move Errors"), args, 1);
2371     }
2372     if (appData.premove) {
2373         XtSetValues(XtNameToWidget(menuBarWidget,
2374                                    "menuOptions.Premove"), args, 1);
2375     }
2376     if (appData.quietPlay) {
2377         XtSetValues(XtNameToWidget(menuBarWidget,
2378                                    "menuOptions.Quiet Play"), args, 1);
2379     }
2380     if (appData.showCoords) {
2381         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2382                     args, 1);
2383     }
2384     if (appData.hideThinkingFromHuman) {
2385         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2386                     args, 1);
2387     }
2388     if (appData.testLegality) {
2389         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2390                     args, 1);
2391     }
2392     if (saveSettingsOnExit) {
2393         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2394                     args, 1);
2395     }
2396
2397     /*
2398      * Create an icon.
2399      */
2400     ReadBitmap(&wIconPixmap, "icon_white.bm",
2401                icon_white_bits, icon_white_width, icon_white_height);
2402     ReadBitmap(&bIconPixmap, "icon_black.bm",
2403                icon_black_bits, icon_black_width, icon_black_height);
2404     iconPixmap = wIconPixmap;
2405     i = 0;
2406     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
2407     XtSetValues(shellWidget, args, i);
2408
2409     /*
2410      * Create a cursor for the board widget.
2411      */
2412     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2413     XChangeWindowAttributes(xDisplay, xBoardWindow,
2414                             CWCursor, &window_attributes);
2415
2416     /*
2417      * Inhibit shell resizing.
2418      */
2419     shellArgs[0].value = (XtArgVal) &w;
2420     shellArgs[1].value = (XtArgVal) &h;
2421     XtGetValues(shellWidget, shellArgs, 2);
2422     shellArgs[4].value = shellArgs[2].value = w;
2423     shellArgs[5].value = shellArgs[3].value = h;
2424     XtSetValues(shellWidget, &shellArgs[2], 4);
2425     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2426     marginH =  h - boardHeight;
2427
2428     CatchDeleteWindow(shellWidget, "QuitProc");
2429
2430     CreateGCs();
2431     CreateGrid();
2432 #if HAVE_LIBXPM
2433     if (appData.bitmapDirectory[0] != NULLCHAR) {
2434       CreatePieces();
2435     } else {
2436       CreateXPMPieces();
2437     }
2438 #else
2439     CreateXIMPieces();
2440     /* Create regular pieces */
2441     if (!useImages) CreatePieces();
2442 #endif
2443
2444     CreatePieceMenus();
2445
2446     if (appData.animate || appData.animateDragging)
2447       CreateAnimVars();
2448
2449     XtAugmentTranslations(formWidget,
2450                           XtParseTranslationTable(globalTranslations));
2451     XtAugmentTranslations(boardWidget,
2452                           XtParseTranslationTable(boardTranslations));
2453     XtAugmentTranslations(whiteTimerWidget,
2454                           XtParseTranslationTable(whiteTranslations));
2455     XtAugmentTranslations(blackTimerWidget,
2456                           XtParseTranslationTable(blackTranslations));
2457
2458     /* Why is the following needed on some versions of X instead
2459      * of a translation? */
2460     XtAddEventHandler(boardWidget, ExposureMask, False,
2461                       (XtEventHandler) EventProc, NULL);
2462     /* end why */
2463
2464     /* [AS] Restore layout */
2465     if( wpMoveHistory.visible ) {
2466       HistoryPopUp();
2467     }
2468
2469 //    if( wpEvalGraph.visible ) {
2470 //      EvalGraphPopUp();
2471 //    }
2472
2473     if( wpEngineOutput.visible ) {
2474       EngineOutputPopUp();
2475     }
2476
2477     InitBackEnd2();
2478
2479     if (errorExitStatus == -1) {
2480         if (appData.icsActive) {
2481             /* We now wait until we see "login:" from the ICS before
2482                sending the logon script (problems with timestamp otherwise) */
2483             /*ICSInitScript();*/
2484             if (appData.icsInputBox) ICSInputBoxPopUp();
2485         }
2486
2487     #ifdef SIGWINCH
2488     signal(SIGWINCH, TermSizeSigHandler);
2489     #endif
2490         signal(SIGINT, IntSigHandler);
2491         signal(SIGTERM, IntSigHandler);
2492         if (*appData.cmailGameName != NULLCHAR) {
2493             signal(SIGUSR1, CmailSigHandler);
2494         }
2495     }
2496     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2497     InitPosition(TRUE);
2498
2499     XtAppMainLoop(appContext);
2500     if (appData.debugMode) fclose(debugFP); // [DM] debug
2501     return 0;
2502 }
2503
2504 void
2505 ShutDownFrontEnd()
2506 {
2507     if (appData.icsActive && oldICSInteractionTitle != NULL) {
2508         DisplayIcsInteractionTitle(oldICSInteractionTitle);
2509     }
2510     if (saveSettingsOnExit) SaveSettings(settingsFileName);
2511     unlink(gameCopyFilename);
2512     unlink(gamePasteFilename);
2513 }
2514
2515 RETSIGTYPE TermSizeSigHandler(int sig)
2516 {
2517     update_ics_width();
2518 }
2519
2520 RETSIGTYPE
2521 IntSigHandler(sig)
2522      int sig;
2523 {
2524     ExitEvent(sig);
2525 }
2526
2527 RETSIGTYPE
2528 CmailSigHandler(sig)
2529      int sig;
2530 {
2531     int dummy = 0;
2532     int error;
2533
2534     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
2535
2536     /* Activate call-back function CmailSigHandlerCallBack()             */
2537     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2538
2539     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2540 }
2541
2542 void
2543 CmailSigHandlerCallBack(isr, closure, message, count, error)
2544      InputSourceRef isr;
2545      VOIDSTAR closure;
2546      char *message;
2547      int count;
2548      int error;
2549 {
2550     BoardToTop();
2551     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
2552 }
2553 /**** end signal code ****/
2554
2555
2556 void
2557 ICSInitScript()
2558 {
2559     FILE *f;
2560     char buf[MSG_SIZ];
2561     char *p;
2562
2563     f = fopen(appData.icsLogon, "r");
2564     if (f == NULL) {
2565         p = getenv("HOME");
2566         if (p != NULL) {
2567             strcpy(buf, p);
2568             strcat(buf, "/");
2569             strcat(buf, appData.icsLogon);
2570             f = fopen(buf, "r");
2571         }
2572     }
2573     if (f != NULL)
2574       ProcessICSInitScript(f);
2575 }
2576
2577 void
2578 ResetFrontEnd()
2579 {
2580     CommentPopDown();
2581     EditCommentPopDown();
2582     TagsPopDown();
2583     return;
2584 }
2585
2586 typedef struct {
2587     char *name;
2588     Boolean value;
2589 } Enables;
2590
2591 void
2592 GreyRevert(grey)
2593      Boolean grey;
2594 {
2595     Widget w;
2596     if (!menuBarWidget) return;
2597     w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
2598     if (w == NULL) {
2599       DisplayError("menuStep.Revert", 0);
2600     } else {
2601       XtSetSensitive(w, !grey);
2602     }
2603 }
2604
2605 void
2606 SetMenuEnables(enab)
2607      Enables *enab;
2608 {
2609   Widget w;
2610   if (!menuBarWidget) return;
2611   while (enab->name != NULL) {
2612     w = XtNameToWidget(menuBarWidget, enab->name);
2613     if (w == NULL) {
2614       DisplayError(enab->name, 0);
2615     } else {
2616       XtSetSensitive(w, enab->value);
2617     }
2618     enab++;
2619   }
2620 }
2621
2622 Enables icsEnables[] = {
2623     { "menuFile.Mail Move", False },
2624     { "menuFile.Reload CMail Message", False },
2625     { "menuMode.Machine Black", False },
2626     { "menuMode.Machine White", False },
2627     { "menuMode.Analysis Mode", False },
2628     { "menuMode.Analyze File", False },
2629     { "menuMode.Two Machines", False },
2630 #ifndef ZIPPY
2631     { "menuHelp.Hint", False },
2632     { "menuHelp.Book", False },
2633     { "menuStep.Move Now", False },
2634     { "menuOptions.Periodic Updates", False },
2635     { "menuOptions.Hide Thinking", False },
2636     { "menuOptions.Ponder Next Move", False },
2637 #endif
2638     { NULL, False }
2639 };
2640
2641 Enables ncpEnables[] = {
2642     { "menuFile.Mail Move", False },
2643     { "menuFile.Reload CMail Message", False },
2644     { "menuMode.Machine White", False },
2645     { "menuMode.Machine Black", False },
2646     { "menuMode.Analysis Mode", False },
2647     { "menuMode.Analyze File", False },
2648     { "menuMode.Two Machines", False },
2649     { "menuMode.ICS Client", False },
2650     { "menuMode.ICS Input Box", False },
2651     { "Action", False },
2652     { "menuStep.Revert", False },
2653     { "menuStep.Move Now", False },
2654     { "menuStep.Retract Move", False },
2655     { "menuOptions.Auto Comment", False },
2656     { "menuOptions.Auto Flag", False },
2657     { "menuOptions.Auto Flip View", False },
2658     { "menuOptions.Auto Observe", False },
2659     { "menuOptions.Auto Raise Board", False },
2660     { "menuOptions.Get Move List", False },
2661     { "menuOptions.ICS Alarm", False },
2662     { "menuOptions.Move Sound", False },
2663     { "menuOptions.Quiet Play", False },
2664     { "menuOptions.Hide Thinking", False },
2665     { "menuOptions.Periodic Updates", False },
2666     { "menuOptions.Ponder Next Move", False },
2667     { "menuHelp.Hint", False },
2668     { "menuHelp.Book", False },
2669     { NULL, False }
2670 };
2671
2672 Enables gnuEnables[] = {
2673     { "menuMode.ICS Client", False },
2674     { "menuMode.ICS Input Box", False },
2675     { "menuAction.Accept", False },
2676     { "menuAction.Decline", False },
2677     { "menuAction.Rematch", False },
2678     { "menuAction.Adjourn", False },
2679     { "menuAction.Stop Examining", False },
2680     { "menuAction.Stop Observing", False },
2681     { "menuStep.Revert", False },
2682     { "menuOptions.Auto Comment", False },
2683     { "menuOptions.Auto Observe", False },
2684     { "menuOptions.Auto Raise Board", False },
2685     { "menuOptions.Get Move List", False },
2686     { "menuOptions.Premove", False },
2687     { "menuOptions.Quiet Play", False },
2688
2689     /* The next two options rely on SetCmailMode being called *after*    */
2690     /* SetGNUMode so that when GNU is being used to give hints these     */
2691     /* menu options are still available                                  */
2692
2693     { "menuFile.Mail Move", False },
2694     { "menuFile.Reload CMail Message", False },
2695     { NULL, False }
2696 };
2697
2698 Enables cmailEnables[] = {
2699     { "Action", True },
2700     { "menuAction.Call Flag", False },
2701     { "menuAction.Draw", True },
2702     { "menuAction.Adjourn", False },
2703     { "menuAction.Abort", False },
2704     { "menuAction.Stop Observing", False },
2705     { "menuAction.Stop Examining", False },
2706     { "menuFile.Mail Move", True },
2707     { "menuFile.Reload CMail Message", True },
2708     { NULL, False }
2709 };
2710
2711 Enables trainingOnEnables[] = {
2712   { "menuMode.Edit Comment", False },
2713   { "menuMode.Pause", False },
2714   { "menuStep.Forward", False },
2715   { "menuStep.Backward", False },
2716   { "menuStep.Forward to End", False },
2717   { "menuStep.Back to Start", False },
2718   { "menuStep.Move Now", False },
2719   { "menuStep.Truncate Game", False },
2720   { NULL, False }
2721 };
2722
2723 Enables trainingOffEnables[] = {
2724   { "menuMode.Edit Comment", True },
2725   { "menuMode.Pause", True },
2726   { "menuStep.Forward", True },
2727   { "menuStep.Backward", True },
2728   { "menuStep.Forward to End", True },
2729   { "menuStep.Back to Start", True },
2730   { "menuStep.Move Now", True },
2731   { "menuStep.Truncate Game", True },
2732   { NULL, False }
2733 };
2734
2735 Enables machineThinkingEnables[] = {
2736   { "menuFile.Load Game", False },
2737   { "menuFile.Load Next Game", False },
2738   { "menuFile.Load Previous Game", False },
2739   { "menuFile.Reload Same Game", False },
2740   { "menuFile.Paste Game", False },
2741   { "menuFile.Load Position", False },
2742   { "menuFile.Load Next Position", False },
2743   { "menuFile.Load Previous Position", False },
2744   { "menuFile.Reload Same Position", False },
2745   { "menuFile.Paste Position", False },
2746   { "menuMode.Machine White", False },
2747   { "menuMode.Machine Black", False },
2748   { "menuMode.Two Machines", False },
2749   { "menuStep.Retract Move", False },
2750   { NULL, False }
2751 };
2752
2753 Enables userThinkingEnables[] = {
2754   { "menuFile.Load Game", True },
2755   { "menuFile.Load Next Game", True },
2756   { "menuFile.Load Previous Game", True },
2757   { "menuFile.Reload Same Game", True },
2758   { "menuFile.Paste Game", True },
2759   { "menuFile.Load Position", True },
2760   { "menuFile.Load Next Position", True },
2761   { "menuFile.Load Previous Position", True },
2762   { "menuFile.Reload Same Position", True },
2763   { "menuFile.Paste Position", True },
2764   { "menuMode.Machine White", True },
2765   { "menuMode.Machine Black", True },
2766   { "menuMode.Two Machines", True },
2767   { "menuStep.Retract Move", True },
2768   { NULL, False }
2769 };
2770
2771 void SetICSMode()
2772 {
2773   SetMenuEnables(icsEnables);
2774
2775 #ifdef ZIPPY
2776   if (appData.zippyPlay && !appData.noChessProgram)   /* [DM] icsEngineAnalyze */
2777      XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2778 #endif
2779 }
2780
2781 void
2782 SetNCPMode()
2783 {
2784   SetMenuEnables(ncpEnables);
2785 }
2786
2787 void
2788 SetGNUMode()
2789 {
2790   SetMenuEnables(gnuEnables);
2791 }
2792
2793 void
2794 SetCmailMode()
2795 {
2796   SetMenuEnables(cmailEnables);
2797 }
2798
2799 void
2800 SetTrainingModeOn()
2801 {
2802   SetMenuEnables(trainingOnEnables);
2803   if (appData.showButtonBar) {
2804     XtSetSensitive(buttonBarWidget, False);
2805   }
2806   CommentPopDown();
2807 }
2808
2809 void
2810 SetTrainingModeOff()
2811 {
2812   SetMenuEnables(trainingOffEnables);
2813   if (appData.showButtonBar) {
2814     XtSetSensitive(buttonBarWidget, True);
2815   }
2816 }
2817
2818 void
2819 SetUserThinkingEnables()
2820 {
2821   if (appData.noChessProgram) return;
2822   SetMenuEnables(userThinkingEnables);
2823 }
2824
2825 void
2826 SetMachineThinkingEnables()
2827 {
2828   if (appData.noChessProgram) return;
2829   SetMenuEnables(machineThinkingEnables);
2830   switch (gameMode) {
2831   case MachinePlaysBlack:
2832   case MachinePlaysWhite:
2833   case TwoMachinesPlay:
2834     XtSetSensitive(XtNameToWidget(menuBarWidget,
2835                                   ModeToWidgetName(gameMode)), True);
2836     break;
2837   default:
2838     break;
2839   }
2840 }
2841
2842 #define Abs(n) ((n)<0 ? -(n) : (n))
2843
2844 /*
2845  * Find a font that matches "pattern" that is as close as
2846  * possible to the targetPxlSize.  Prefer fonts that are k
2847  * pixels smaller to fonts that are k pixels larger.  The
2848  * pattern must be in the X Consortium standard format,
2849  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2850  * The return value should be freed with XtFree when no
2851  * longer needed.
2852  */
2853 char *FindFont(pattern, targetPxlSize)
2854      char *pattern;
2855      int targetPxlSize;
2856 {
2857     char **fonts, *p, *best, *scalable, *scalableTail;
2858     int i, j, nfonts, minerr, err, pxlSize;
2859
2860 #ifdef ENABLE_NLS
2861     char **missing_list;
2862     int missing_count;
2863     char *def_string, *base_fnt_lst, strInt[3];
2864     XFontSet fntSet;
2865     XFontStruct **fnt_list;
2866
2867     base_fnt_lst = calloc(1, strlen(pattern) + 3);
2868     sprintf(strInt, "%d", targetPxlSize);
2869     p = strstr(pattern, "--");
2870     strncpy(base_fnt_lst, pattern, p - pattern + 2);
2871     strcat(base_fnt_lst, strInt);
2872     strcat(base_fnt_lst, strchr(p + 2, '-'));
2873
2874     if ((fntSet = XCreateFontSet(xDisplay,
2875                                  base_fnt_lst,
2876                                  &missing_list,
2877                                  &missing_count,
2878                                  &def_string)) == NULL) {
2879
2880        fprintf(stderr, _("Unable to create font set.\n"));
2881        exit (2);
2882     }
2883
2884     nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
2885 #else
2886     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2887     if (nfonts < 1) {
2888         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2889                 programName, pattern);
2890         exit(2);
2891     }
2892 #endif
2893
2894     best = fonts[0];
2895     scalable = NULL;
2896     minerr = 999999;
2897     for (i=0; i<nfonts; i++) {
2898         j = 0;
2899         p = fonts[i];
2900         if (*p != '-') continue;
2901         while (j < 7) {
2902             if (*p == NULLCHAR) break;
2903             if (*p++ == '-') j++;
2904         }
2905         if (j < 7) continue;
2906         pxlSize = atoi(p);
2907         if (pxlSize == 0) {
2908             scalable = fonts[i];
2909             scalableTail = p;
2910         } else {
2911             err = pxlSize - targetPxlSize;
2912             if (Abs(err) < Abs(minerr) ||
2913                 (minerr > 0 && err < 0 && -err == minerr)) {
2914                 best = fonts[i];
2915                 minerr = err;
2916             }
2917         }
2918     }
2919     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2920         /* If the error is too big and there is a scalable font,
2921            use the scalable font. */
2922         int headlen = scalableTail - scalable;
2923         p = (char *) XtMalloc(strlen(scalable) + 10);
2924         while (isdigit(*scalableTail)) scalableTail++;
2925         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2926     } else {
2927         p = (char *) XtMalloc(strlen(best) + 1);
2928         strcpy(p, best);
2929     }
2930     if (appData.debugMode) {
2931         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
2932                 pattern, targetPxlSize, p);
2933     }
2934 #ifdef ENABLE_NLS
2935     if (missing_count > 0)
2936        XFreeStringList(missing_list);
2937     XFreeFontSet(xDisplay, fntSet);
2938 #else
2939      XFreeFontNames(fonts);
2940 #endif
2941     return p;
2942 }
2943
2944 void CreateGCs()
2945 {
2946     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2947       | GCBackground | GCFunction | GCPlaneMask;
2948     XGCValues gc_values;
2949     GC copyInvertedGC;
2950
2951     gc_values.plane_mask = AllPlanes;
2952     gc_values.line_width = lineGap;
2953     gc_values.line_style = LineSolid;
2954     gc_values.function = GXcopy;
2955
2956     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2957     gc_values.background = XBlackPixel(xDisplay, xScreen);
2958     lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2959
2960     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2961     gc_values.background = XWhitePixel(xDisplay, xScreen);
2962     coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
2963     XSetFont(xDisplay, coordGC, coordFontID);
2964
2965     // [HGM] make font for holdings counts (white on black0
2966     gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2967     gc_values.background = XBlackPixel(xDisplay, xScreen);
2968     countGC = XtGetGC(shellWidget, value_mask, &gc_values);
2969     XSetFont(xDisplay, countGC, countFontID);
2970
2971     if (appData.monoMode) {
2972         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2973         gc_values.background = XWhitePixel(xDisplay, xScreen);
2974         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2975
2976         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2977         gc_values.background = XBlackPixel(xDisplay, xScreen);
2978         lightSquareGC = wbPieceGC
2979           = XtGetGC(shellWidget, value_mask, &gc_values);
2980
2981         gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2982         gc_values.background = XWhitePixel(xDisplay, xScreen);
2983         darkSquareGC = bwPieceGC
2984           = XtGetGC(shellWidget, value_mask, &gc_values);
2985
2986         if (DefaultDepth(xDisplay, xScreen) == 1) {
2987             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2988             gc_values.function = GXcopyInverted;
2989             copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
2990             gc_values.function = GXcopy;
2991             if (XBlackPixel(xDisplay, xScreen) == 1) {
2992                 bwPieceGC = darkSquareGC;
2993                 wbPieceGC = copyInvertedGC;
2994             } else {
2995                 bwPieceGC = copyInvertedGC;
2996                 wbPieceGC = lightSquareGC;
2997             }
2998         }
2999     } else {
3000         gc_values.foreground = highlightSquareColor;
3001         gc_values.background = highlightSquareColor;
3002         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3003
3004         gc_values.foreground = premoveHighlightColor;
3005         gc_values.background = premoveHighlightColor;
3006         prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3007
3008         gc_values.foreground = lightSquareColor;
3009         gc_values.background = darkSquareColor;
3010         lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3011
3012         gc_values.foreground = darkSquareColor;
3013         gc_values.background = lightSquareColor;
3014         darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3015
3016         gc_values.foreground = jailSquareColor;
3017         gc_values.background = jailSquareColor;
3018         jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3019
3020         gc_values.foreground = whitePieceColor;
3021         gc_values.background = darkSquareColor;
3022         wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3023
3024         gc_values.foreground = whitePieceColor;
3025         gc_values.background = lightSquareColor;
3026         wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3027
3028         gc_values.foreground = whitePieceColor;
3029         gc_values.background = jailSquareColor;
3030         wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3031
3032         gc_values.foreground = blackPieceColor;
3033         gc_values.background = darkSquareColor;
3034         bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3035
3036         gc_values.foreground = blackPieceColor;
3037         gc_values.background = lightSquareColor;
3038         blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3039
3040         gc_values.foreground = blackPieceColor;
3041         gc_values.background = jailSquareColor;
3042         bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3043     }
3044 }
3045
3046 void loadXIM(xim, xmask, filename, dest, mask)
3047      XImage *xim;
3048      XImage *xmask;
3049      char *filename;
3050      Pixmap *dest;
3051      Pixmap *mask;
3052 {
3053     int x, y, w, h, p;
3054     FILE *fp;
3055     Pixmap temp;
3056     XGCValues   values;
3057     GC maskGC;
3058
3059     fp = fopen(filename, "rb");
3060     if (!fp) {
3061         fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3062         exit(1);
3063     }
3064
3065     w = fgetc(fp);
3066     h = fgetc(fp);
3067
3068     for (y=0; y<h; ++y) {
3069         for (x=0; x<h; ++x) {
3070             p = fgetc(fp);
3071
3072             switch (p) {
3073               case 0:
3074                 XPutPixel(xim, x, y, blackPieceColor);
3075                 if (xmask)
3076                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3077                 break;
3078               case 1:
3079                 XPutPixel(xim, x, y, darkSquareColor);
3080                 if (xmask)
3081                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3082                 break;
3083               case 2:
3084                 XPutPixel(xim, x, y, whitePieceColor);
3085                 if (xmask)
3086                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3087                 break;
3088               case 3:
3089                 XPutPixel(xim, x, y, lightSquareColor);
3090                 if (xmask)
3091                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3092                 break;
3093             }
3094         }
3095     }
3096
3097     /* create Pixmap of piece */
3098     *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3099                           w, h, xim->depth);
3100     XPutImage(xDisplay, *dest, lightSquareGC, xim,
3101               0, 0, 0, 0, w, h);
3102
3103     /* create Pixmap of clipmask
3104        Note: We assume the white/black pieces have the same
3105              outline, so we make only 6 masks. This is okay
3106              since the XPM clipmask routines do the same. */
3107     if (xmask) {
3108       temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3109                             w, h, xim->depth);
3110       XPutImage(xDisplay, temp, lightSquareGC, xmask,
3111               0, 0, 0, 0, w, h);
3112
3113       /* now create the 1-bit version */
3114       *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3115                           w, h, 1);
3116
3117       values.foreground = 1;
3118       values.background = 0;
3119
3120       /* Don't use XtGetGC, not read only */
3121       maskGC = XCreateGC(xDisplay, *mask,
3122                     GCForeground | GCBackground, &values);
3123       XCopyPlane(xDisplay, temp, *mask, maskGC,
3124                   0, 0, squareSize, squareSize, 0, 0, 1);
3125       XFreePixmap(xDisplay, temp);
3126     }
3127 }
3128
3129
3130 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3131
3132 void CreateXIMPieces()
3133 {
3134     int piece, kind;
3135     char buf[MSG_SIZ];
3136     u_int ss;
3137     static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3138     XImage *ximtemp;
3139
3140     ss = squareSize;
3141
3142     /* The XSynchronize calls were copied from CreatePieces.
3143        Not sure if needed, but can't hurt */
3144     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3145                                      buffering bug */
3146
3147     /* temp needed by loadXIM() */
3148     ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3149                  0, 0, ss, ss, AllPlanes, XYPixmap);
3150
3151     if (strlen(appData.pixmapDirectory) == 0) {
3152       useImages = 0;
3153     } else {
3154         useImages = 1;
3155         if (appData.monoMode) {
3156           DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3157                             0, 2);
3158           ExitEvent(2);
3159         }
3160         fprintf(stderr, _("\nLoading XIMs...\n"));
3161         /* Load pieces */
3162         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3163             fprintf(stderr, "%d", piece+1);
3164             for (kind=0; kind<4; kind++) {
3165                 fprintf(stderr, ".");
3166                 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3167                         ExpandPathName(appData.pixmapDirectory),
3168                         piece <= (int) WhiteKing ? "" : "w",
3169                         pieceBitmapNames[piece],
3170                         ximkind[kind], ss);
3171                 ximPieceBitmap[kind][piece] =
3172                   XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3173                             0, 0, ss, ss, AllPlanes, XYPixmap);
3174                 if (appData.debugMode)
3175                   fprintf(stderr, _("(File:%s:) "), buf);
3176                 loadXIM(ximPieceBitmap[kind][piece],
3177                         ximtemp, buf,
3178                         &(xpmPieceBitmap2[kind][piece]),
3179                         &(ximMaskPm2[piece]));
3180                 if(piece <= (int)WhiteKing)
3181                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3182             }
3183             fprintf(stderr," ");
3184         }
3185         /* Load light and dark squares */
3186         /* If the LSQ and DSQ pieces don't exist, we will
3187            draw them with solid squares. */
3188         snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3189         if (access(buf, 0) != 0) {
3190             useImageSqs = 0;
3191         } else {
3192             useImageSqs = 1;
3193             fprintf(stderr, _("light square "));
3194             ximLightSquare=
3195               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3196                         0, 0, ss, ss, AllPlanes, XYPixmap);
3197             if (appData.debugMode)
3198               fprintf(stderr, _("(File:%s:) "), buf);
3199
3200             loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3201             fprintf(stderr, _("dark square "));
3202             snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3203                     ExpandPathName(appData.pixmapDirectory), ss);
3204             if (appData.debugMode)
3205               fprintf(stderr, _("(File:%s:) "), buf);
3206             ximDarkSquare=
3207               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3208                         0, 0, ss, ss, AllPlanes, XYPixmap);
3209             loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3210             xpmJailSquare = xpmLightSquare;
3211         }
3212         fprintf(stderr, _("Done.\n"));
3213     }
3214     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3215 }
3216
3217 #if HAVE_LIBXPM
3218 void CreateXPMPieces()
3219 {
3220     int piece, kind, r;
3221     char buf[MSG_SIZ];
3222     u_int ss = squareSize;
3223     XpmAttributes attr;
3224     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3225     XpmColorSymbol symbols[4];
3226
3227     /* The XSynchronize calls were copied from CreatePieces.
3228        Not sure if needed, but can't hurt */
3229     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3230
3231     /* Setup translations so piece colors match square colors */
3232     symbols[0].name = "light_piece";
3233     symbols[0].value = appData.whitePieceColor;
3234     symbols[1].name = "dark_piece";
3235     symbols[1].value = appData.blackPieceColor;
3236     symbols[2].name = "light_square";
3237     symbols[2].value = appData.lightSquareColor;
3238     symbols[3].name = "dark_square";
3239     symbols[3].value = appData.darkSquareColor;
3240
3241     attr.valuemask = XpmColorSymbols;
3242     attr.colorsymbols = symbols;
3243     attr.numsymbols = 4;
3244
3245     if (appData.monoMode) {
3246       DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3247                         0, 2);
3248       ExitEvent(2);
3249     }
3250     if (strlen(appData.pixmapDirectory) == 0) {
3251         XpmPieces* pieces = builtInXpms;
3252         useImages = 1;
3253         /* Load pieces */
3254         while (pieces->size != squareSize && pieces->size) pieces++;
3255         if (!pieces->size) {
3256           fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3257           exit(1);
3258         }
3259         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3260             for (kind=0; kind<4; kind++) {
3261
3262                 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3263                                                pieces->xpm[piece][kind],
3264                                                &(xpmPieceBitmap2[kind][piece]),
3265                                                NULL, &attr)) != 0) {
3266                   fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3267                           r, buf);
3268                   exit(1);
3269                 }
3270                 if(piece <= (int) WhiteKing)
3271                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3272             }
3273         }
3274         useImageSqs = 0;
3275         xpmJailSquare = xpmLightSquare;
3276     } else {
3277         useImages = 1;
3278
3279         fprintf(stderr, _("\nLoading XPMs...\n"));
3280
3281         /* Load pieces */
3282         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3283             fprintf(stderr, "%d ", piece+1);
3284             for (kind=0; kind<4; kind++) {
3285               snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3286                         ExpandPathName(appData.pixmapDirectory),
3287                         piece > (int) WhiteKing ? "w" : "",
3288                         pieceBitmapNames[piece],
3289                         xpmkind[kind], ss);
3290                 if (appData.debugMode) {
3291                     fprintf(stderr, _("(File:%s:) "), buf);
3292                 }
3293                 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3294                                            &(xpmPieceBitmap2[kind][piece]),
3295                                            NULL, &attr)) != 0) {
3296                     if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3297                       // [HGM] missing: read of unorthodox piece failed; substitute King.
3298                       snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3299                                 ExpandPathName(appData.pixmapDirectory),
3300                                 xpmkind[kind], ss);
3301                         if (appData.debugMode) {
3302                             fprintf(stderr, _("(Replace by File:%s:) "), buf);
3303                         }
3304                         r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3305                                                 &(xpmPieceBitmap2[kind][piece]),
3306                                                 NULL, &attr);
3307                     }
3308                     if (r != 0) {
3309                         fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3310                                 r, buf);
3311                         exit(1);
3312                     }
3313                 }
3314                 if(piece <= (int) WhiteKing) 
3315                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3316             }
3317         }
3318         /* Load light and dark squares */
3319         /* If the LSQ and DSQ pieces don't exist, we will
3320            draw them with solid squares. */
3321         fprintf(stderr, _("light square "));
3322         snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3323         if (access(buf, 0) != 0) {
3324             useImageSqs = 0;
3325         } else {
3326             useImageSqs = 1;
3327             if (appData.debugMode)
3328               fprintf(stderr, _("(File:%s:) "), buf);
3329
3330             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3331                                        &xpmLightSquare, NULL, &attr)) != 0) {
3332                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3333                 exit(1);
3334             }
3335             fprintf(stderr, _("dark square "));
3336             snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3337                     ExpandPathName(appData.pixmapDirectory), ss);
3338             if (appData.debugMode) {
3339                 fprintf(stderr, _("(File:%s:) "), buf);
3340             }
3341             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3342                                        &xpmDarkSquare, NULL, &attr)) != 0) {
3343                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3344                 exit(1);
3345             }
3346         }
3347         xpmJailSquare = xpmLightSquare;
3348         fprintf(stderr, _("Done.\n"));
3349     }
3350     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3351                                       buffering bug */
3352 }
3353 #endif /* HAVE_LIBXPM */
3354
3355 #if HAVE_LIBXPM
3356 /* No built-in bitmaps */
3357 void CreatePieces()
3358 {
3359     int piece, kind;
3360     char buf[MSG_SIZ];
3361     u_int ss = squareSize;
3362
3363     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3364                                      buffering bug */
3365
3366     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3367         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3368             sprintf(buf, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3369                     pieceBitmapNames[piece],
3370                     ss, kind == SOLID ? 's' : 'o');
3371             ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3372             if(piece <= (int)WhiteKing)
3373                 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3374         }
3375     }
3376
3377     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3378                                       buffering bug */
3379 }
3380 #else
3381 /* With built-in bitmaps */
3382 void CreatePieces()
3383 {
3384     BuiltInBits* bib = builtInBits;
3385     int piece, kind;
3386     char buf[MSG_SIZ];
3387     u_int ss = squareSize;
3388
3389     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3390                                      buffering bug */
3391
3392     while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3393
3394     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3395         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3396             sprintf(buf, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3397                     pieceBitmapNames[piece],
3398                     ss, kind == SOLID ? 's' : 'o');
3399             ReadBitmap(&pieceBitmap2[kind][piece], buf,
3400                        bib->bits[kind][piece], ss, ss);
3401             if(piece <= (int)WhiteKing)
3402                 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3403         }
3404     }
3405
3406     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3407                                       buffering bug */
3408 }
3409 #endif
3410
3411 void ReadBitmap(pm, name, bits, wreq, hreq)
3412      Pixmap *pm;
3413      String name;
3414      unsigned char bits[];
3415      u_int wreq, hreq;
3416 {
3417     int x_hot, y_hot;
3418     u_int w, h;
3419     int errcode;
3420     char msg[MSG_SIZ], fullname[MSG_SIZ];
3421
3422     if (*appData.bitmapDirectory != NULLCHAR) {
3423         strcpy(fullname, appData.bitmapDirectory);
3424         strcat(fullname, "/");
3425         strcat(fullname, name);
3426         errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3427                                   &w, &h, pm, &x_hot, &y_hot);
3428     fprintf(stderr, "load %s\n", name);
3429         if (errcode != BitmapSuccess) {
3430             switch (errcode) {
3431               case BitmapOpenFailed:
3432                 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3433                 break;
3434               case BitmapFileInvalid:
3435                 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3436                 break;
3437               case BitmapNoMemory:
3438                 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3439                         fullname);
3440                 break;
3441               default:
3442                 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3443                         errcode, fullname);
3444                 break;
3445             }
3446             fprintf(stderr, _("%s: %s...using built-in\n"),
3447                     programName, msg);
3448         } else if (w != wreq || h != hreq) {
3449             fprintf(stderr,
3450                     _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3451                     programName, fullname, w, h, wreq, hreq);
3452         } else {
3453             return;
3454         }
3455     }
3456     if (bits != NULL) {
3457         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3458                                     wreq, hreq);
3459     }
3460 }
3461
3462 void CreateGrid()
3463 {
3464     int i, j;
3465
3466     if (lineGap == 0) return;
3467
3468     /* [HR] Split this into 2 loops for non-square boards. */
3469
3470     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3471         gridSegments[i].x1 = 0;
3472         gridSegments[i].x2 =
3473           lineGap + BOARD_WIDTH * (squareSize + lineGap);
3474         gridSegments[i].y1 = gridSegments[i].y2
3475           = lineGap / 2 + (i * (squareSize + lineGap));
3476     }
3477
3478     for (j = 0; j < BOARD_WIDTH + 1; j++) {
3479         gridSegments[j + i].y1 = 0;
3480         gridSegments[j + i].y2 =
3481           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3482         gridSegments[j + i].x1 = gridSegments[j + i].x2
3483           = lineGap / 2 + (j * (squareSize + lineGap));
3484     }
3485 }
3486
3487 static void MenuBarSelect(w, addr, index)
3488      Widget w;
3489      caddr_t addr;
3490      caddr_t index;
3491 {
3492     XtActionProc proc = (XtActionProc) addr;
3493
3494     (proc)(NULL, NULL, NULL, NULL);
3495 }
3496
3497 void CreateMenuBarPopup(parent, name, mb)
3498      Widget parent;
3499      String name;
3500      Menu *mb;
3501 {
3502     int j;
3503     Widget menu, entry;
3504     MenuItem *mi;
3505     Arg args[16];
3506
3507     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3508                               parent, NULL, 0);
3509     j = 0;
3510     XtSetArg(args[j], XtNleftMargin, 20);   j++;
3511     XtSetArg(args[j], XtNrightMargin, 20);  j++;
3512     mi = mb->mi;
3513     while (mi->string != NULL) {
3514         if (strcmp(mi->string, "----") == 0) {
3515             entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3516                                           menu, args, j);
3517         } else {
3518           XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3519             entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
3520                                           menu, args, j+1);
3521             XtAddCallback(entry, XtNcallback,
3522                           (XtCallbackProc) MenuBarSelect,
3523                           (caddr_t) mi->proc);
3524         }
3525         mi++;
3526     }
3527 }
3528
3529 Widget CreateMenuBar(mb)
3530      Menu *mb;
3531 {
3532     int j;
3533     Widget anchor, menuBar;
3534     Arg args[16];
3535     char menuName[MSG_SIZ];
3536
3537     j = 0;
3538     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
3539     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3540     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3541     menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3542                              formWidget, args, j);
3543
3544     while (mb->name != NULL) {
3545         strcpy(menuName, "menu");
3546         strcat(menuName, mb->name);
3547         j = 0;
3548         XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
3549         if (tinyLayout) {
3550             char shortName[2];
3551             shortName[0] = _(mb->name)[0];
3552             shortName[1] = NULLCHAR;
3553             XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3554         }
3555       else {
3556           XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3557       }
3558
3559         XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3560         anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3561                                        menuBar, args, j);
3562         CreateMenuBarPopup(menuBar, menuName, mb);
3563         mb++;
3564     }
3565     return menuBar;
3566 }
3567
3568 Widget CreateButtonBar(mi)
3569      MenuItem *mi;
3570 {
3571     int j;
3572     Widget button, buttonBar;
3573     Arg args[16];
3574
3575     j = 0;
3576     XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3577     if (tinyLayout) {
3578         XtSetArg(args[j], XtNhSpace, 0); j++;
3579     }
3580     XtSetArg(args[j], XtNborderWidth, 0); j++;
3581     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3582     buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3583                                formWidget, args, j);
3584
3585     while (mi->string != NULL) {
3586         j = 0;
3587         if (tinyLayout) {
3588             XtSetArg(args[j], XtNinternalWidth, 2); j++;
3589             XtSetArg(args[j], XtNborderWidth, 0); j++;
3590         }
3591       XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3592         button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3593                                        buttonBar, args, j);
3594         XtAddCallback(button, XtNcallback,
3595                       (XtCallbackProc) MenuBarSelect,
3596                       (caddr_t) mi->proc);
3597         mi++;
3598     }
3599     return buttonBar;
3600 }
3601
3602 Widget
3603 CreatePieceMenu(name, color)
3604      char *name;
3605      int color;
3606 {
3607     int i;
3608     Widget entry, menu;
3609     Arg args[16];
3610     ChessSquare selection;
3611
3612     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3613                               boardWidget, args, 0);
3614
3615     for (i = 0; i < PIECE_MENU_SIZE; i++) {
3616         String item = pieceMenuStrings[color][i];
3617
3618         if (strcmp(item, "----") == 0) {
3619             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3620                                           menu, NULL, 0);
3621         } else {
3622           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3623             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3624                                 menu, args, 1);
3625             selection = pieceMenuTranslation[color][i];
3626             XtAddCallback(entry, XtNcallback,
3627                           (XtCallbackProc) PieceMenuSelect,
3628                           (caddr_t) selection);
3629             if (selection == WhitePawn || selection == BlackPawn) {
3630                 XtSetArg(args[0], XtNpopupOnEntry, entry);
3631                 XtSetValues(menu, args, 1);
3632             }
3633         }
3634     }
3635     return menu;
3636 }
3637
3638 void
3639 CreatePieceMenus()
3640 {
3641     int i;
3642     Widget entry;
3643     Arg args[16];
3644     ChessSquare selection;
3645
3646     whitePieceMenu = CreatePieceMenu("menuW", 0);
3647     blackPieceMenu = CreatePieceMenu("menuB", 1);
3648
3649     XtRegisterGrabAction(PieceMenuPopup, True,
3650                          (unsigned)(ButtonPressMask|ButtonReleaseMask),
3651                          GrabModeAsync, GrabModeAsync);
3652
3653     XtSetArg(args[0], XtNlabel, _("Drop"));
3654     dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3655                                   boardWidget, args, 1);
3656     for (i = 0; i < DROP_MENU_SIZE; i++) {
3657         String item = dropMenuStrings[i];
3658
3659         if (strcmp(item, "----") == 0) {
3660             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3661                                           dropMenu, NULL, 0);
3662         } else {
3663           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3664             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3665                                 dropMenu, args, 1);
3666             selection = dropMenuTranslation[i];
3667             XtAddCallback(entry, XtNcallback,
3668                           (XtCallbackProc) DropMenuSelect,
3669                           (caddr_t) selection);
3670         }
3671     }
3672 }
3673
3674 void SetupDropMenu()
3675 {
3676     int i, j, count;
3677     char label[32];
3678     Arg args[16];
3679     Widget entry;
3680     char* p;
3681
3682     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3683         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3684         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3685                    dmEnables[i].piece);
3686         XtSetSensitive(entry, p != NULL || !appData.testLegality
3687                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3688                                        && !appData.icsActive));
3689         count = 0;
3690         while (p && *p++ == dmEnables[i].piece) count++;
3691         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
3692         j = 0;
3693         XtSetArg(args[j], XtNlabel, label); j++;
3694         XtSetValues(entry, args, j);
3695     }
3696 }
3697
3698 void PieceMenuPopup(w, event, params, num_params)
3699      Widget w;
3700      XEvent *event;
3701      String *params;
3702      Cardinal *num_params;
3703 {
3704     String whichMenu;
3705     if (event->type != ButtonPress) return;
3706     if (errorUp) ErrorPopDown();
3707     switch (gameMode) {
3708       case EditPosition:
3709       case IcsExamining:
3710         whichMenu = params[0];
3711         break;
3712       case IcsPlayingWhite:
3713       case IcsPlayingBlack:
3714       case EditGame:
3715       case MachinePlaysWhite:
3716       case MachinePlaysBlack:
3717         if (appData.testLegality &&
3718             gameInfo.variant != VariantBughouse &&
3719             gameInfo.variant != VariantCrazyhouse) return;
3720         SetupDropMenu();
3721         whichMenu = "menuD";
3722         break;
3723       default:
3724         return;
3725     }
3726
3727     if (((pmFromX = EventToSquare(event->xbutton.x, BOARD_WIDTH)) < 0) ||
3728         ((pmFromY = EventToSquare(event->xbutton.y, BOARD_HEIGHT)) < 0)) {
3729         pmFromX = pmFromY = -1;
3730         return;
3731     }
3732     if (flipView)
3733       pmFromX = BOARD_WIDTH - 1 - pmFromX;
3734     else
3735       pmFromY = BOARD_HEIGHT - 1 - pmFromY;
3736
3737     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3738 }
3739
3740 static void PieceMenuSelect(w, piece, junk)
3741      Widget w;
3742      ChessSquare piece;
3743      caddr_t junk;
3744 {
3745     if (pmFromX < 0 || pmFromY < 0) return;
3746     EditPositionMenuEvent(piece, pmFromX, pmFromY);
3747 }
3748
3749 static void DropMenuSelect(w, piece, junk)
3750      Widget w;
3751      ChessSquare piece;
3752      caddr_t junk;
3753 {
3754     if (pmFromX < 0 || pmFromY < 0) return;
3755     DropMenuEvent(piece, pmFromX, pmFromY);
3756 }
3757
3758 void WhiteClock(w, event, prms, nprms)
3759      Widget w;
3760      XEvent *event;
3761      String *prms;
3762      Cardinal *nprms;
3763 {
3764     if (gameMode == EditPosition || gameMode == IcsExamining) {
3765         SetWhiteToPlayEvent();
3766     } else if (gameMode == IcsPlayingBlack || gameMode == MachinePlaysWhite) {
3767         CallFlagEvent();
3768     }
3769 }
3770
3771 void BlackClock(w, event, prms, nprms)
3772      Widget w;
3773      XEvent *event;
3774      String *prms;
3775      Cardinal *nprms;
3776 {
3777     if (gameMode == EditPosition || gameMode == IcsExamining) {
3778         SetBlackToPlayEvent();
3779     } else if (gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) {
3780         CallFlagEvent();
3781     }
3782 }
3783
3784
3785 /*
3786  * If the user selects on a border boundary, return -1; if off the board,
3787  *   return -2.  Otherwise map the event coordinate to the square.
3788  */
3789 int EventToSquare(x, limit)
3790      int x;
3791 {
3792     if (x <= 0)
3793       return -2;
3794     if (x < lineGap)
3795       return -1;
3796     x -= lineGap;
3797     if ((x % (squareSize + lineGap)) >= squareSize)
3798       return -1;
3799     x /= (squareSize + lineGap);
3800     if (x >= limit)
3801       return -2;
3802     return x;
3803 }
3804
3805 static void do_flash_delay(msec)
3806      unsigned long msec;
3807 {
3808     TimeDelay(msec);
3809 }
3810
3811 static void drawHighlight(file, rank, gc)
3812      int file, rank;
3813      GC gc;
3814 {
3815     int x, y;
3816
3817     if (lineGap == 0 || appData.blindfold) return;
3818
3819     if (flipView) {
3820         x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
3821           (squareSize + lineGap);
3822         y = lineGap/2 + rank * (squareSize + lineGap);
3823     } else {
3824         x = lineGap/2 + file * (squareSize + lineGap);
3825         y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
3826           (squareSize + lineGap);
3827     }
3828
3829     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3830                    squareSize+lineGap, squareSize+lineGap);
3831 }
3832
3833 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
3834 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
3835
3836 void
3837 SetHighlights(fromX, fromY, toX, toY)
3838      int fromX, fromY, toX, toY;
3839 {
3840     if (hi1X != fromX || hi1Y != fromY) {
3841         if (hi1X >= 0 && hi1Y >= 0) {
3842             drawHighlight(hi1X, hi1Y, lineGC);
3843         }
3844         if (fromX >= 0 && fromY >= 0) {
3845             drawHighlight(fromX, fromY, highlineGC);
3846         }
3847     }
3848     if (hi2X != toX || hi2Y != toY) {
3849         if (hi2X >= 0 && hi2Y >= 0) {
3850             drawHighlight(hi2X, hi2Y, lineGC);
3851         }
3852         if (toX >= 0 && toY >= 0) {
3853             drawHighlight(toX, toY, highlineGC);
3854         }
3855     }
3856     hi1X = fromX;
3857     hi1Y = fromY;
3858     hi2X = toX;
3859     hi2Y = toY;
3860 }
3861
3862 void
3863 ClearHighlights()
3864 {
3865     SetHighlights(-1, -1, -1, -1);
3866 }
3867
3868
3869 void
3870 SetPremoveHighlights(fromX, fromY, toX, toY)
3871      int fromX, fromY, toX, toY;
3872 {
3873     if (pm1X != fromX || pm1Y != fromY) {
3874         if (pm1X >= 0 && pm1Y >= 0) {
3875             drawHighlight(pm1X, pm1Y, lineGC);
3876         }
3877         if (fromX >= 0 && fromY >= 0) {
3878             drawHighlight(fromX, fromY, prelineGC);
3879         }
3880     }
3881     if (pm2X != toX || pm2Y != toY) {
3882         if (pm2X >= 0 && pm2Y >= 0) {
3883             drawHighlight(pm2X, pm2Y, lineGC);
3884         }
3885         if (toX >= 0 && toY >= 0) {
3886             drawHighlight(toX, toY, prelineGC);
3887         }
3888     }
3889     pm1X = fromX;
3890     pm1Y = fromY;
3891     pm2X = toX;
3892     pm2Y = toY;
3893 }
3894
3895 void
3896 ClearPremoveHighlights()
3897 {
3898   SetPremoveHighlights(-1, -1, -1, -1);
3899 }
3900
3901 static void BlankSquare(x, y, color, piece, dest)
3902      int x, y, color;
3903      ChessSquare piece;
3904      Drawable dest;
3905 {
3906     if (useImages && useImageSqs) {
3907         Pixmap pm;
3908         switch (color) {
3909           case 1: /* light */
3910             pm = xpmLightSquare;
3911             break;
3912           case 0: /* dark */
3913             pm = xpmDarkSquare;
3914             break;
3915           case 2: /* neutral */
3916           default:
3917             pm = xpmJailSquare;
3918             break;
3919         }
3920         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3921                   squareSize, squareSize, x, y);
3922     } else {
3923         GC gc;
3924         switch (color) {
3925           case 1: /* light */
3926             gc = lightSquareGC;
3927             break;
3928           case 0: /* dark */
3929             gc = darkSquareGC;
3930             break;
3931           case 2: /* neutral */
3932           default:
3933             gc = jailSquareGC;
3934             break;
3935         }
3936         XFillRectangle(xDisplay, dest, gc, x, y, squareSize, squareSize);
3937     }
3938 }
3939
3940 /*
3941    I split out the routines to draw a piece so that I could
3942    make a generic flash routine.
3943 */
3944 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
3945      ChessSquare piece;
3946      int square_color, x, y;
3947      Drawable dest;
3948 {
3949     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3950     switch (square_color) {
3951       case 1: /* light */
3952       case 2: /* neutral */
3953       default:
3954         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3955                   ? *pieceToOutline(piece)
3956                   : *pieceToSolid(piece),
3957                   dest, bwPieceGC, 0, 0,
3958                   squareSize, squareSize, x, y);
3959         break;
3960       case 0: /* dark */
3961         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3962                   ? *pieceToSolid(piece)
3963                   : *pieceToOutline(piece),
3964                   dest, wbPieceGC, 0, 0,
3965                   squareSize, squareSize, x, y);
3966         break;
3967     }
3968 }
3969
3970 static void monoDrawPiece(piece, square_color, x, y, dest)
3971      ChessSquare piece;
3972      int square_color, x, y;
3973      Drawable dest;
3974 {
3975     switch (square_color) {
3976       case 1: /* light */
3977       case 2: /* neutral */
3978       default:
3979         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3980                    ? *pieceToOutline(piece)
3981                    : *pieceToSolid(piece),
3982                    dest, bwPieceGC, 0, 0,
3983                    squareSize, squareSize, x, y, 1);
3984         break;
3985       case 0: /* dark */
3986         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3987                    ? *pieceToSolid(piece)
3988                    : *pieceToOutline(piece),
3989                    dest, wbPieceGC, 0, 0,
3990                    squareSize, squareSize, x, y, 1);
3991         break;
3992     }
3993 }
3994
3995 static void colorDrawPiece(piece, square_color, x, y, dest)
3996      ChessSquare piece;
3997      int square_color, x, y;
3998      Drawable dest;
3999 {
4000     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4001     switch (square_color) {
4002       case 1: /* light */
4003         XCopyPlane(xDisplay, *pieceToSolid(piece),
4004                    dest, (int) piece < (int) BlackPawn
4005                    ? wlPieceGC : blPieceGC, 0, 0,
4006                    squareSize, squareSize, x, y, 1);
4007         break;
4008       case 0: /* dark */
4009         XCopyPlane(xDisplay, *pieceToSolid(piece),
4010                    dest, (int) piece < (int) BlackPawn
4011                    ? wdPieceGC : bdPieceGC, 0, 0,
4012                    squareSize, squareSize, x, y, 1);
4013         break;
4014       case 2: /* neutral */
4015       default:
4016         XCopyPlane(xDisplay, *pieceToSolid(piece),
4017                    dest, (int) piece < (int) BlackPawn
4018                    ? wjPieceGC : bjPieceGC, 0, 0,
4019                    squareSize, squareSize, x, y, 1);
4020         break;
4021     }
4022 }
4023
4024 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4025      ChessSquare piece;
4026      int square_color, x, y;
4027      Drawable dest;
4028 {
4029     int kind;
4030
4031     switch (square_color) {
4032       case 1: /* light */
4033       case 2: /* neutral */
4034       default:
4035         if ((int)piece < (int) BlackPawn) {
4036             kind = 0;
4037         } else {
4038             kind = 2;
4039             piece -= BlackPawn;
4040         }
4041         break;
4042       case 0: /* dark */
4043         if ((int)piece < (int) BlackPawn) {
4044             kind = 1;
4045         } else {
4046             kind = 3;
4047             piece -= BlackPawn;
4048         }
4049         break;
4050     }
4051     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4052               dest, wlPieceGC, 0, 0,
4053               squareSize, squareSize, x, y);
4054 }
4055
4056 typedef void (*DrawFunc)();
4057
4058 DrawFunc ChooseDrawFunc()
4059 {
4060     if (appData.monoMode) {
4061         if (DefaultDepth(xDisplay, xScreen) == 1) {
4062             return monoDrawPiece_1bit;
4063         } else {
4064             return monoDrawPiece;
4065         }
4066     } else {
4067         if (useImages)
4068           return colorDrawPieceImage;
4069         else
4070           return colorDrawPiece;
4071     }
4072 }
4073
4074 /* [HR] determine square color depending on chess variant. */
4075 static int SquareColor(row, column)
4076      int row, column;
4077 {
4078     int square_color;
4079
4080     if (gameInfo.variant == VariantXiangqi) {
4081         if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4082             square_color = 1;
4083         } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4084             square_color = 0;
4085         } else if (row <= 4) {
4086             square_color = 0;
4087         } else {
4088             square_color = 1;
4089         }
4090     } else {
4091         square_color = ((column + row) % 2) == 1;
4092     }
4093
4094     /* [hgm] holdings: next line makes all holdings squares light */
4095     if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4096
4097     return square_color;
4098 }
4099
4100 void DrawSquare(row, column, piece, do_flash)
4101      int row, column, do_flash;
4102      ChessSquare piece;
4103 {
4104     int square_color, x, y, direction, font_ascent, font_descent;
4105     int i;
4106     char string[2];
4107     XCharStruct overall;
4108     DrawFunc drawfunc;
4109     int flash_delay;
4110
4111     /* Calculate delay in milliseconds (2-delays per complete flash) */
4112     flash_delay = 500 / appData.flashRate;
4113
4114     if (flipView) {
4115         x = lineGap + ((BOARD_WIDTH-1)-column) *
4116           (squareSize + lineGap);
4117         y = lineGap + row * (squareSize + lineGap);
4118     } else {
4119         x = lineGap + column * (squareSize + lineGap);
4120         y = lineGap + ((BOARD_HEIGHT-1)-row) *
4121           (squareSize + lineGap);
4122     }
4123
4124     square_color = SquareColor(row, column);
4125
4126     if ( // [HGM] holdings: blank out area between board and holdings
4127                  column == BOARD_LEFT-1 ||  column == BOARD_RGHT
4128               || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4129                   || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4130                         BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
4131
4132                         // [HGM] print piece counts next to holdings
4133                         string[1] = NULLCHAR;
4134                         if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4135                             string[0] = '0' + piece;
4136                             XTextExtents(countFontStruct, string, 1, &direction,
4137                                  &font_ascent, &font_descent, &overall);
4138                             if (appData.monoMode) {
4139                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4140                                                  x + squareSize - overall.width - 2,
4141                                                  y + font_ascent + 1, string, 1);
4142                             } else {
4143                                 XDrawString(xDisplay, xBoardWindow, countGC,
4144                                             x + squareSize - overall.width - 2,
4145                                             y + font_ascent + 1, string, 1);
4146                             }
4147                         }
4148                         if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4149                             string[0] = '0' + piece;
4150                             XTextExtents(countFontStruct, string, 1, &direction,
4151                                          &font_ascent, &font_descent, &overall);
4152                             if (appData.monoMode) {
4153                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4154                                                  x + 2, y + font_ascent + 1, string, 1);
4155                             } else {
4156                                 XDrawString(xDisplay, xBoardWindow, countGC,
4157                                             x + 2, y + font_ascent + 1, string, 1);
4158                             }
4159                         }
4160     } else {
4161             if (piece == EmptySquare || appData.blindfold) {
4162                         BlankSquare(x, y, square_color, piece, xBoardWindow);
4163             } else {
4164                         drawfunc = ChooseDrawFunc();
4165                         if (do_flash && appData.flashCount > 0) {
4166                             for (i=0; i<appData.flashCount; ++i) {
4167
4168                                         drawfunc(piece, square_color, x, y, xBoardWindow);
4169                                         XSync(xDisplay, False);
4170                                         do_flash_delay(flash_delay);
4171
4172                                         BlankSquare(x, y, square_color, piece, xBoardWindow);
4173                                         XSync(xDisplay, False);
4174                                         do_flash_delay(flash_delay);
4175                             }
4176                         }
4177                         drawfunc(piece, square_color, x, y, xBoardWindow);
4178         }
4179         }
4180
4181     string[1] = NULLCHAR;
4182     if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4183                 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4184         string[0] = 'a' + column - BOARD_LEFT;
4185         XTextExtents(coordFontStruct, string, 1, &direction,
4186                      &font_ascent, &font_descent, &overall);
4187         if (appData.monoMode) {
4188             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4189                              x + squareSize - overall.width - 2,
4190                              y + squareSize - font_descent - 1, string, 1);
4191         } else {
4192             XDrawString(xDisplay, xBoardWindow, coordGC,
4193                         x + squareSize - overall.width - 2,
4194                         y + squareSize - font_descent - 1, string, 1);
4195         }
4196     }
4197     if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4198         string[0] = ONE + row;
4199         XTextExtents(coordFontStruct, string, 1, &direction,
4200                      &font_ascent, &font_descent, &overall);
4201         if (appData.monoMode) {
4202             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4203                              x + 2, y + font_ascent + 1, string, 1);
4204         } else {
4205             XDrawString(xDisplay, xBoardWindow, coordGC,
4206                         x + 2, y + font_ascent + 1, string, 1);
4207         }
4208     }
4209 }
4210
4211
4212 /* Why is this needed on some versions of X? */
4213 void EventProc(widget, unused, event)
4214      Widget widget;
4215      caddr_t unused;
4216      XEvent *event;
4217 {
4218     if (!XtIsRealized(widget))
4219       return;
4220
4221     switch (event->type) {
4222       case Expose:
4223         if (event->xexpose.count > 0) return;  /* no clipping is done */
4224         XDrawPosition(widget, True, NULL);
4225         break;
4226       default:
4227         return;
4228     }
4229 }
4230 /* end why */
4231
4232 void DrawPosition(fullRedraw, board)
4233      /*Boolean*/int fullRedraw;
4234      Board board;
4235 {
4236     XDrawPosition(boardWidget, fullRedraw, board);
4237 }
4238
4239 /* Returns 1 if there are "too many" differences between b1 and b2
4240    (i.e. more than 1 move was made) */
4241 static int too_many_diffs(b1, b2)
4242      Board b1, b2;
4243 {
4244     int i, j;
4245     int c = 0;
4246
4247     for (i=0; i<BOARD_HEIGHT; ++i) {
4248         for (j=0; j<BOARD_WIDTH; ++j) {
4249             if (b1[i][j] != b2[i][j]) {