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