Delete old indirection settings-file code
[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 DEBUG_FILE "xboard.debug"
1253 #define SetCurrentDirectory chdir
1254 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1255 #define OPTCHAR "-"
1256 #define SEPCHAR " "
1257
1258 // these two must some day move to frontend.h, when they are implemented
1259 Boolean MoveHistoryIsUp();
1260 Boolean GameListIsUp();
1261
1262 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1263 #include "args.h"
1264
1265 // front-end part of option handling
1266
1267 // [HGM] This platform-dependent table provides the location for storing the color info
1268 extern char *crWhite, * crBlack;
1269
1270 void *
1271 colorVariable[] = {
1272   &appData.whitePieceColor, 
1273   &appData.blackPieceColor, 
1274   &appData.lightSquareColor,
1275   &appData.darkSquareColor, 
1276   &appData.highlightSquareColor,
1277   &appData.premoveHighlightColor,
1278   NULL,
1279   NULL,
1280   NULL,
1281   NULL,
1282   NULL,
1283   NULL,
1284   &crWhite,
1285   &crBlack,
1286   NULL
1287 };
1288
1289 void
1290 ParseFont(char *name, int number)
1291 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1292   switch(number) {
1293     case 0: // CLOCK_FONT
1294         appData.clockFont = strdup(name);
1295       break;
1296     case 1: // MESSAGE_FONT
1297         appData.font = strdup(name);
1298       break;
1299     case 2: // COORD_FONT
1300         appData.coordFont = strdup(name);
1301       break;
1302     default:
1303       return;
1304   }
1305 }
1306
1307 void
1308 SetFontDefaults()
1309 { // only 2 fonts currently
1310   appData.clockFont = CLOCK_FONT_NAME;
1311   appData.coordFont = COORD_FONT_NAME;
1312   appData.font  =   DEFAULT_FONT_NAME;
1313 }
1314
1315 void
1316 CreateFonts()
1317 { // no-op, until we identify the code for this already in XBoard and move it here
1318 }
1319
1320 void
1321 ParseColor(int n, char *name)
1322 { // in XBoard, just copy the color-name string
1323   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1324 }
1325
1326 void
1327 ParseTextAttribs(ColorClass cc, char *s)
1328 {   
1329     (&appData.colorShout)[cc] = strdup(s);
1330 }
1331
1332 void
1333 ParseBoardSize(void *addr, char *name)
1334 {
1335     appData.boardSize = strdup(name);
1336 }
1337
1338 void
1339 LoadAllSounds()
1340 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1341 }
1342
1343 void
1344 SetCommPortDefaults()
1345 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1346 }
1347
1348 // [HGM] args: these three cases taken out to stay in front-end
1349 void
1350 SaveFontArg(FILE *f, ArgDescriptor *ad)
1351 {
1352   char *name;
1353   switch((int)ad->argLoc) {
1354     case 0: // CLOCK_FONT
1355         name = appData.clockFont;
1356       break;
1357     case 1: // MESSAGE_FONT
1358         name = appData.font;
1359       break;
1360     case 2: // COORD_FONT
1361         name = appData.coordFont;
1362       break;
1363     default:
1364       return;
1365   }
1366 //  Do not save fonts for now, as the saved font would be board-size specific
1367 //  and not suitable for a re-start at another board size
1368 //  fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, name); 
1369 }
1370
1371 void
1372 ExportSounds()
1373 { // nothing to do, as the sounds are at all times represented by their text-string names already
1374 }
1375
1376 void
1377 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1378 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1379         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
1380 }
1381
1382 void
1383 SaveColor(FILE *f, ArgDescriptor *ad)
1384 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1385         if(colorVariable[(int)ad->argLoc])
1386         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
1387 }
1388
1389 void
1390 SaveBoardSize(FILE *f, char *name, void *addr)
1391 { // wrapper to shield back-end from BoardSize & sizeInfo
1392   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1393 }
1394
1395 void
1396 ParseCommPortSettings(char *s)
1397 { // no such option in XBoard (yet)
1398 }
1399
1400 extern Widget engineOutputShell;
1401 extern Widget tagsShell, editTagsShell;
1402 void
1403 GetActualPlacement(Widget wg, WindowPlacement *wp)
1404 {
1405   Arg args[16];
1406   Dimension w, h;
1407   Position x, y;
1408   int i;
1409
1410   if(!wg) return;
1411   
1412     i = 0;
1413     XtSetArg(args[i], XtNx, &x); i++;
1414     XtSetArg(args[i], XtNy, &y); i++;
1415     XtSetArg(args[i], XtNwidth, &w); i++;
1416     XtSetArg(args[i], XtNheight, &h); i++;
1417     XtGetValues(wg, args, i);
1418     wp->x = x - 4;
1419     wp->y = y - 23;
1420     wp->height = h;
1421     wp->width = w;
1422 }
1423
1424 void
1425 GetWindowCoords()
1426 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1427   // In XBoard this will have to wait until awareness of window parameters is implemented
1428   GetActualPlacement(shellWidget, &wpMain);
1429   if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1430   if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1431   if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1432   if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1433   if(commentShell) GetActualPlacement(commentShell, &wpComment);
1434   else             GetActualPlacement(editShell,    &wpComment);
1435   if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1436   else      GetActualPlacement(editTagsShell, &wpTags);
1437 }
1438
1439 void
1440 PrintCommPortSettings(FILE *f, char *name)
1441 { // This option does not exist in XBoard
1442 }
1443
1444 int
1445 MySearchPath(char *installDir, char *name, char *fullname)
1446 { // just append installDir and name. Perhaps ExpandPath should be used here?
1447   name = ExpandPathName(name);
1448   if(name && name[0] == '/') strcpy(fullname, name); else {
1449     sprintf(fullname, "%s%c%s", installDir, '/', name);
1450   }
1451   return 1;
1452 }
1453
1454 int
1455 MyGetFullPathName(char *name, char *fullname)
1456 { // should use ExpandPath?
1457   name = ExpandPathName(name);
1458   strcpy(fullname, name);
1459   return 1;
1460 }
1461
1462 void
1463 EnsureOnScreen(int *x, int *y, int minX, int minY)
1464 {
1465   return;
1466 }
1467
1468 int
1469 MainWindowUp()
1470 { // [HGM] args: allows testing if main window is realized from back-end
1471   return xBoardWindow != 0;
1472 }
1473
1474 void
1475 PopUpStartupDialog()
1476 {  // start menu not implemented in XBoard
1477 }
1478 char *
1479 ConvertToLine(int argc, char **argv)
1480 {
1481   static char line[128*1024], buf[1024];
1482   int i;
1483
1484   line[0] = NULLCHAR;
1485   for(i=1; i<argc; i++) {
1486     if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1487         && argv[i][0] != '{' )
1488          sprintf(buf, "{%s} ", argv[i]);
1489     else sprintf(buf, "%s ", argv[i]);
1490     strcat(line, buf);
1491   }
1492     line[strlen(line)-1] = NULLCHAR;
1493   return line;
1494 }
1495
1496 //--------------------------------------------------------------------------------------------
1497
1498 #ifdef IDSIZES
1499   // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1500 #else
1501 #define BoardSize int
1502 void InitDrawingSizes(BoardSize boardSize, int flags)
1503 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1504     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1505     Arg args[16];
1506     XtGeometryResult gres;
1507     int i;
1508
1509     if(!formWidget) return;
1510
1511     /*
1512      * Enable shell resizing.
1513      */
1514     shellArgs[0].value = (XtArgVal) &w;
1515     shellArgs[1].value = (XtArgVal) &h;
1516     XtGetValues(shellWidget, shellArgs, 2);
1517
1518     shellArgs[4].value = 2*w; shellArgs[2].value = 10;
1519     shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1520     XtSetValues(shellWidget, &shellArgs[2], 4);
1521
1522     XtSetArg(args[0], XtNdefaultDistance, &sep);
1523     XtGetValues(formWidget, args, 1);
1524
1525     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1526     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1527     CreateGrid();
1528
1529     XtSetArg(args[0], XtNwidth, boardWidth);
1530     XtSetArg(args[1], XtNheight, boardHeight);
1531     XtSetValues(boardWidget, args, 2);
1532
1533     timerWidth = (boardWidth - sep) / 2;
1534     XtSetArg(args[0], XtNwidth, timerWidth);
1535     XtSetValues(whiteTimerWidget, args, 1);
1536     XtSetValues(blackTimerWidget, args, 1);
1537
1538     XawFormDoLayout(formWidget, False);
1539
1540     if (appData.titleInWindow) {
1541         i = 0;
1542         XtSetArg(args[i], XtNborderWidth, &bor); i++;
1543         XtSetArg(args[i], XtNheight, &h);  i++;
1544         XtGetValues(titleWidget, args, i);
1545         if (smallLayout) {
1546             w = boardWidth - 2*bor;
1547         } else {
1548             XtSetArg(args[0], XtNwidth, &w);
1549             XtGetValues(menuBarWidget, args, 1);
1550             w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1551         }
1552
1553         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1554         if (gres != XtGeometryYes && appData.debugMode) {
1555             fprintf(stderr,
1556                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1557                     programName, gres, w, h, wr, hr);
1558         }
1559     }
1560
1561     XawFormDoLayout(formWidget, True);
1562
1563     /*
1564      * Inhibit shell resizing.
1565      */
1566     shellArgs[0].value = w = (XtArgVal) boardWidth + marginW;
1567     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1568     shellArgs[4].value = shellArgs[2].value = w;
1569     shellArgs[5].value = shellArgs[3].value = h;
1570     XtSetValues(shellWidget, &shellArgs[0], 6);
1571
1572     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1573     // (only for xpm)
1574     if(useImages) {
1575       for(i=0; i<4; i++) {
1576         int p;
1577         for(p=0; p<=(int)WhiteKing; p++)
1578            xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1579         if(gameInfo.variant == VariantShogi) {
1580            xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1581            xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1582            xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1583            xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1584            xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1585         }
1586 #ifdef GOTHIC
1587         if(gameInfo.variant == VariantGothic) {
1588            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1589         }
1590 #endif
1591 #if !HAVE_LIBXPM
1592         // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1593         for(p=0; p<=(int)WhiteKing; p++)
1594            ximMaskPm[p] = ximMaskPm2[p]; // defaults
1595         if(gameInfo.variant == VariantShogi) {
1596            ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1597            ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1598            ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1599            ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1600            ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1601         }
1602 #ifdef GOTHIC
1603         if(gameInfo.variant == VariantGothic) {
1604            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1605         }
1606 #endif
1607 #endif
1608       }
1609     } else {
1610       for(i=0; i<2; i++) {
1611         int p;
1612         for(p=0; p<=(int)WhiteKing; p++)
1613            pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1614         if(gameInfo.variant == VariantShogi) {
1615            pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1616            pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1617            pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1618            pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1619            pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1620         }
1621 #ifdef GOTHIC
1622         if(gameInfo.variant == VariantGothic) {
1623            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1624         }
1625 #endif
1626       }
1627     }
1628 #if HAVE_LIBXPM
1629     CreateAnimVars();
1630 #endif
1631 }
1632 #endif
1633
1634 void EscapeExpand(char *p, char *q)
1635 {       // [HGM] initstring: routine to shape up string arguments
1636         while(*p++ = *q++) if(p[-1] == '\\')
1637             switch(*q++) {
1638                 case 'n': p[-1] = '\n'; break;
1639                 case 'r': p[-1] = '\r'; break;
1640                 case 't': p[-1] = '\t'; break;
1641                 case '\\': p[-1] = '\\'; break;
1642                 case 0: *p = 0; return;
1643                 default: p[-1] = q[-1]; break;
1644             }
1645 }
1646
1647 int
1648 main(argc, argv)
1649      int argc;
1650      char **argv;
1651 {
1652     int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1653     XSetWindowAttributes window_attributes;
1654     Arg args[16];
1655     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1656     XrmValue vFrom, vTo;
1657     XtGeometryResult gres;
1658     char *p;
1659     XrmDatabase xdb;
1660     int forceMono = False;
1661
1662     srandom(time(0)); // [HGM] book: make random truly random
1663
1664     setbuf(stdout, NULL);
1665     setbuf(stderr, NULL);
1666     debugFP = stderr;
1667
1668     programName = strrchr(argv[0], '/');
1669     if (programName == NULL)
1670       programName = argv[0];
1671     else
1672       programName++;
1673
1674 #ifdef ENABLE_NLS
1675     XtSetLanguageProc(NULL, NULL, NULL);
1676     bindtextdomain(PACKAGE, LOCALEDIR);
1677     textdomain(PACKAGE);
1678 #endif
1679
1680     shellWidget =
1681       XtAppInitialize(&appContext, "XBoard", shellOptions,
1682                       XtNumber(shellOptions),
1683                       &argc, argv, xboardResources, NULL, 0);
1684     appData.boardSize = "";
1685     InitAppData(ConvertToLine(argc, argv));
1686     p = getenv("HOME");
1687     if (p == NULL) p = "/tmp";
1688     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1689     gameCopyFilename = (char*) malloc(i);
1690     gamePasteFilename = (char*) malloc(i);
1691     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1692     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1693
1694     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1695                               clientResources, XtNumber(clientResources),
1696                               NULL, 0);
1697
1698     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1699         static char buf[MSG_SIZ];
1700         EscapeExpand(buf, appData.initString);
1701         appData.initString = strdup(buf);
1702         EscapeExpand(buf, appData.secondInitString);
1703         appData.secondInitString = strdup(buf);
1704         EscapeExpand(buf, appData.firstComputerString);
1705         appData.firstComputerString = strdup(buf);
1706         EscapeExpand(buf, appData.secondComputerString);
1707         appData.secondComputerString = strdup(buf);
1708     }
1709
1710     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1711         chessDir = ".";
1712     } else {
1713         if (chdir(chessDir) != 0) {
1714             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1715             perror(chessDir);
1716             exit(1);
1717         }
1718     }
1719
1720     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1721         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1722         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
1723            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1724            exit(errno);
1725         }
1726         setbuf(debugFP, NULL);
1727     }
1728
1729     /* [HGM,HR] make sure board size is acceptable */
1730     if(appData.NrFiles > BOARD_FILES ||
1731        appData.NrRanks > BOARD_RANKS   )
1732          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1733
1734 #if !HIGHDRAG
1735     /* This feature does not work; animation needs a rewrite */
1736     appData.highlightDragging = FALSE;
1737 #endif
1738     InitBackEnd1();
1739
1740     xDisplay = XtDisplay(shellWidget);
1741     xScreen = DefaultScreen(xDisplay);
1742     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1743
1744         gameInfo.variant = StringToVariant(appData.variant);
1745         InitPosition(FALSE);
1746
1747 #ifdef IDSIZE
1748     InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1749 #else
1750     if (isdigit(appData.boardSize[0])) {
1751         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1752                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1753                    &fontPxlSize, &smallLayout, &tinyLayout);
1754         if (i == 0) {
1755             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1756                     programName, appData.boardSize);
1757             exit(2);
1758         }
1759         if (i < 7) {
1760             /* Find some defaults; use the nearest known size */
1761             SizeDefaults *szd, *nearest;
1762             int distance = 99999;
1763             nearest = szd = sizeDefaults;
1764             while (szd->name != NULL) {
1765                 if (abs(szd->squareSize - squareSize) < distance) {
1766                     nearest = szd;
1767                     distance = abs(szd->squareSize - squareSize);
1768                     if (distance == 0) break;
1769                 }
1770                 szd++;
1771             }
1772             if (i < 2) lineGap = nearest->lineGap;
1773             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1774             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1775             if (i < 5) fontPxlSize = nearest->fontPxlSize;
1776             if (i < 6) smallLayout = nearest->smallLayout;
1777             if (i < 7) tinyLayout = nearest->tinyLayout;
1778         }
1779     } else {
1780         SizeDefaults *szd = sizeDefaults;
1781         if (*appData.boardSize == NULLCHAR) {
1782             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1783                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1784               szd++;
1785             }
1786             if (szd->name == NULL) szd--;
1787             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1788         } else {
1789             while (szd->name != NULL &&
1790                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1791             if (szd->name == NULL) {
1792                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1793                         programName, appData.boardSize);
1794                 exit(2);
1795             }
1796         }
1797         squareSize = szd->squareSize;
1798         lineGap = szd->lineGap;
1799         clockFontPxlSize = szd->clockFontPxlSize;
1800         coordFontPxlSize = szd->coordFontPxlSize;
1801         fontPxlSize = szd->fontPxlSize;
1802         smallLayout = szd->smallLayout;
1803         tinyLayout = szd->tinyLayout;
1804     }
1805
1806     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1807     if (strlen(appData.pixmapDirectory) > 0) {
1808         p = ExpandPathName(appData.pixmapDirectory);
1809         if (!p) {
1810             fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1811                    appData.pixmapDirectory);
1812             exit(1);
1813         }
1814         if (appData.debugMode) {
1815           fprintf(stderr, _("\
1816 XBoard square size (hint): %d\n\
1817 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1818         }
1819         squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1820         if (appData.debugMode) {
1821             fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1822         }
1823     }
1824
1825     /* [HR] height treated separately (hacked) */
1826     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1827     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1828     if (appData.showJail == 1) {
1829         /* Jail on top and bottom */
1830         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1831         XtSetArg(boardArgs[2], XtNheight,
1832                  boardHeight + 2*(lineGap + squareSize));
1833     } else if (appData.showJail == 2) {
1834         /* Jail on sides */
1835         XtSetArg(boardArgs[1], XtNwidth,
1836                  boardWidth + 2*(lineGap + squareSize));
1837         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1838     } else {
1839         /* No jail */
1840         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1841         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1842     }
1843
1844     /*
1845      * Determine what fonts to use.
1846      */
1847     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1848     clockFontID = XLoadFont(xDisplay, appData.clockFont);
1849     clockFontStruct = XQueryFont(xDisplay, clockFontID);
1850     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1851     coordFontID = XLoadFont(xDisplay, appData.coordFont);
1852     coordFontStruct = XQueryFont(xDisplay, coordFontID);
1853     appData.font = FindFont(appData.font, fontPxlSize);
1854     countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
1855     countFontStruct = XQueryFont(xDisplay, countFontID);
1856 //    appData.font = FindFont(appData.font, fontPxlSize);
1857
1858     xdb = XtDatabase(xDisplay);
1859     XrmPutStringResource(&xdb, "*font", appData.font);
1860
1861     /*
1862      * Detect if there are not enough colors available and adapt.
1863      */
1864     if (DefaultDepth(xDisplay, xScreen) <= 2) {
1865       appData.monoMode = True;
1866     }
1867
1868     if (!appData.monoMode) {
1869         vFrom.addr = (caddr_t) appData.lightSquareColor;
1870         vFrom.size = strlen(appData.lightSquareColor);
1871         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1872         if (vTo.addr == NULL) {
1873           appData.monoMode = True;
1874           forceMono = True;
1875         } else {
1876           lightSquareColor = *(Pixel *) vTo.addr;
1877         }
1878     }
1879     if (!appData.monoMode) {
1880         vFrom.addr = (caddr_t) appData.darkSquareColor;
1881         vFrom.size = strlen(appData.darkSquareColor);
1882         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1883         if (vTo.addr == NULL) {
1884           appData.monoMode = True;
1885           forceMono = True;
1886         } else {
1887           darkSquareColor = *(Pixel *) vTo.addr;
1888         }
1889     }
1890     if (!appData.monoMode) {
1891         vFrom.addr = (caddr_t) appData.whitePieceColor;
1892         vFrom.size = strlen(appData.whitePieceColor);
1893         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1894         if (vTo.addr == NULL) {
1895           appData.monoMode = True;
1896           forceMono = True;
1897         } else {
1898           whitePieceColor = *(Pixel *) vTo.addr;
1899         }
1900     }
1901     if (!appData.monoMode) {
1902         vFrom.addr = (caddr_t) appData.blackPieceColor;
1903         vFrom.size = strlen(appData.blackPieceColor);
1904         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1905         if (vTo.addr == NULL) {
1906           appData.monoMode = True;
1907           forceMono = True;
1908         } else {
1909           blackPieceColor = *(Pixel *) vTo.addr;
1910         }
1911     }
1912
1913     if (!appData.monoMode) {
1914         vFrom.addr = (caddr_t) appData.highlightSquareColor;
1915         vFrom.size = strlen(appData.highlightSquareColor);
1916         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1917         if (vTo.addr == NULL) {
1918           appData.monoMode = True;
1919           forceMono = True;
1920         } else {
1921           highlightSquareColor = *(Pixel *) vTo.addr;
1922         }
1923     }
1924
1925     if (!appData.monoMode) {
1926         vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1927         vFrom.size = strlen(appData.premoveHighlightColor);
1928         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1929         if (vTo.addr == NULL) {
1930           appData.monoMode = True;
1931           forceMono = True;
1932         } else {
1933           premoveHighlightColor = *(Pixel *) vTo.addr;
1934         }
1935     }
1936
1937     if (forceMono) {
1938       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1939               programName);
1940       
1941       if (appData.bitmapDirectory == NULL ||
1942               appData.bitmapDirectory[0] == NULLCHAR)
1943             appData.bitmapDirectory = DEF_BITMAP_DIR;
1944     }
1945
1946     if (appData.lowTimeWarning && !appData.monoMode) {
1947       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1948       vFrom.size = strlen(appData.lowTimeWarningColor);
1949       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1950       if (vTo.addr == NULL) 
1951                 appData.monoMode = True;
1952       else
1953                 lowTimeWarningColor = *(Pixel *) vTo.addr;
1954     }
1955
1956     if (appData.monoMode && appData.debugMode) {
1957         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1958                 (unsigned long) XWhitePixel(xDisplay, xScreen),
1959                 (unsigned long) XBlackPixel(xDisplay, xScreen));
1960     }
1961
1962     if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1963         parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1964         parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
1965         parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
1966         parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1967         parse_cpair(ColorTell, appData.colorTell) < 0 ||
1968         parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
1969         parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
1970         parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
1971         parse_cpair(ColorNormal, appData.colorNormal) < 0)
1972       {
1973           if (appData.colorize) {
1974               fprintf(stderr,
1975                       _("%s: can't parse color names; disabling colorization\n"),
1976                       programName);
1977           }
1978           appData.colorize = FALSE;
1979       }
1980     textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1981     textColors[ColorNone].attr = 0;
1982
1983     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1984
1985     /*
1986      * widget hierarchy
1987      */
1988     if (tinyLayout) {
1989         layoutName = "tinyLayout";
1990     } else if (smallLayout) {
1991         layoutName = "smallLayout";
1992     } else {
1993         layoutName = "normalLayout";
1994     }
1995     /* Outer layoutWidget is there only to provide a name for use in
1996        resources that depend on the layout style */
1997     layoutWidget =
1998       XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1999                             layoutArgs, XtNumber(layoutArgs));
2000     formWidget =
2001       XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2002                             formArgs, XtNumber(formArgs));
2003     XtSetArg(args[0], XtNdefaultDistance, &sep);
2004     XtGetValues(formWidget, args, 1);
2005
2006     j = 0;
2007     widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2008     XtSetArg(args[0], XtNtop,    XtChainTop);
2009     XtSetArg(args[1], XtNbottom, XtChainTop);
2010     XtSetArg(args[2], XtNright,  XtChainLeft);
2011     XtSetValues(menuBarWidget, args, 3);
2012
2013     widgetList[j++] = whiteTimerWidget =
2014       XtCreateWidget("whiteTime", labelWidgetClass,
2015                      formWidget, timerArgs, XtNumber(timerArgs));
2016     XtSetArg(args[0], XtNfont, clockFontStruct);
2017     XtSetArg(args[1], XtNtop,    XtChainTop);
2018     XtSetArg(args[2], XtNbottom, XtChainTop);
2019     XtSetValues(whiteTimerWidget, args, 3);
2020
2021     widgetList[j++] = blackTimerWidget =
2022       XtCreateWidget("blackTime", labelWidgetClass,
2023                      formWidget, timerArgs, XtNumber(timerArgs));
2024     XtSetArg(args[0], XtNfont, clockFontStruct);
2025     XtSetArg(args[1], XtNtop,    XtChainTop);
2026     XtSetArg(args[2], XtNbottom, XtChainTop);
2027     XtSetValues(blackTimerWidget, args, 3);
2028
2029     if (appData.titleInWindow) {
2030         widgetList[j++] = titleWidget =
2031           XtCreateWidget("title", labelWidgetClass, formWidget,
2032                          titleArgs, XtNumber(titleArgs));
2033         XtSetArg(args[0], XtNtop,    XtChainTop);
2034         XtSetArg(args[1], XtNbottom, XtChainTop);
2035         XtSetValues(titleWidget, args, 2);
2036     }
2037
2038     if (appData.showButtonBar) {
2039       widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2040       XtSetArg(args[0], XtNleft,  XtChainRight); // [HGM] glue to right window edge
2041       XtSetArg(args[1], XtNright, XtChainRight); //       for good run-time sizing
2042       XtSetArg(args[2], XtNtop,    XtChainTop);
2043       XtSetArg(args[3], XtNbottom, XtChainTop);
2044       XtSetValues(buttonBarWidget, args, 4);
2045     }
2046
2047     widgetList[j++] = messageWidget =
2048       XtCreateWidget("message", labelWidgetClass, formWidget,
2049                      messageArgs, XtNumber(messageArgs));
2050     XtSetArg(args[0], XtNtop,    XtChainTop);
2051     XtSetArg(args[1], XtNbottom, XtChainTop);
2052     XtSetValues(messageWidget, args, 2);
2053
2054     widgetList[j++] = boardWidget =
2055       XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2056                      XtNumber(boardArgs));
2057
2058     XtManageChildren(widgetList, j);
2059
2060     timerWidth = (boardWidth - sep) / 2;
2061     XtSetArg(args[0], XtNwidth, timerWidth);
2062     XtSetValues(whiteTimerWidget, args, 1);
2063     XtSetValues(blackTimerWidget, args, 1);
2064
2065     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2066     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2067     XtGetValues(whiteTimerWidget, args, 2);
2068
2069     if (appData.showButtonBar) {
2070       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2071       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2072       XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2073     }
2074
2075     /*
2076      * formWidget uses these constraints but they are stored
2077      * in the children.
2078      */
2079     i = 0;
2080     XtSetArg(args[i], XtNfromHoriz, 0); i++;
2081     XtSetValues(menuBarWidget, args, i);
2082     if (appData.titleInWindow) {
2083         if (smallLayout) {
2084             i = 0;
2085             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2086             XtSetValues(whiteTimerWidget, args, i);
2087             i = 0;
2088             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2089             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2090             XtSetValues(blackTimerWidget, args, i);
2091             i = 0;
2092             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2093             XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2094             XtSetValues(titleWidget, args, i);
2095             i = 0;
2096             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2097             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2098             XtSetValues(messageWidget, args, i);
2099             if (appData.showButtonBar) {
2100               i = 0;
2101               XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2102               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2103               XtSetValues(buttonBarWidget, args, i);
2104             }
2105         } else {
2106             i = 0;
2107             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2108             XtSetValues(whiteTimerWidget, args, i);
2109             i = 0;
2110             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2111             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2112             XtSetValues(blackTimerWidget, args, i);
2113             i = 0;
2114             XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2115             XtSetValues(titleWidget, args, i);
2116             i = 0;
2117             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2118             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2119             XtSetValues(messageWidget, args, i);
2120             if (appData.showButtonBar) {
2121               i = 0;
2122               XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2123               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2124               XtSetValues(buttonBarWidget, args, i);
2125             }
2126         }
2127     } else {
2128         i = 0;
2129         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2130         XtSetValues(whiteTimerWidget, args, i);
2131         i = 0;
2132         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2133         XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2134         XtSetValues(blackTimerWidget, args, i);
2135         i = 0;
2136         XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2137         XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2138         XtSetValues(messageWidget, args, i);
2139         if (appData.showButtonBar) {
2140           i = 0;
2141           XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2142           XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2143           XtSetValues(buttonBarWidget, args, i);
2144         }
2145     }
2146     i = 0;
2147     XtSetArg(args[0], XtNfromVert, messageWidget);
2148     XtSetArg(args[1], XtNtop,    XtChainTop);
2149     XtSetArg(args[2], XtNbottom, XtChainBottom);
2150     XtSetArg(args[3], XtNleft,   XtChainLeft);
2151     XtSetArg(args[4], XtNright,  XtChainRight);
2152     XtSetValues(boardWidget, args, 5);
2153
2154     XtRealizeWidget(shellWidget);
2155
2156     if(wpMain.x > 0) {
2157       XtSetArg(args[0], XtNx, wpMain.x);
2158       XtSetArg(args[1], XtNy, wpMain.y);
2159       XtSetValues(shellWidget, args, 2);
2160     }
2161
2162     /*
2163      * Correct the width of the message and title widgets.
2164      * It is not known why some systems need the extra fudge term.
2165      * The value "2" is probably larger than needed.
2166      */
2167     XawFormDoLayout(formWidget, False);
2168
2169 #define WIDTH_FUDGE 2
2170     i = 0;
2171     XtSetArg(args[i], XtNborderWidth, &bor);  i++;
2172     XtSetArg(args[i], XtNheight, &h);  i++;
2173     XtGetValues(messageWidget, args, i);
2174     if (appData.showButtonBar) {
2175       i = 0;
2176       XtSetArg(args[i], XtNwidth, &w);  i++;
2177       XtGetValues(buttonBarWidget, args, i);
2178       w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2179     } else {
2180       w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2181     }
2182
2183     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2184     if (gres != XtGeometryYes && appData.debugMode) {
2185       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2186               programName, gres, w, h, wr, hr);
2187     }
2188
2189     /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2190     /* The size used for the child widget in layout lags one resize behind
2191        its true size, so we resize a second time, 1 pixel smaller.  Yeech! */
2192     w--;
2193     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2194     if (gres != XtGeometryYes && appData.debugMode) {
2195       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2196               programName, gres, w, h, wr, hr);
2197     }
2198     /* !! end hack */
2199     XtSetArg(args[0], XtNleft,  XtChainLeft);  // [HGM] glue ends for good run-time sizing
2200     XtSetArg(args[1], XtNright, XtChainRight);
2201     XtSetValues(messageWidget, args, 2);
2202
2203     if (appData.titleInWindow) {
2204         i = 0;
2205         XtSetArg(args[i], XtNborderWidth, &bor); i++;
2206         XtSetArg(args[i], XtNheight, &h);  i++;
2207         XtGetValues(titleWidget, args, i);
2208         if (smallLayout) {
2209             w = boardWidth - 2*bor;
2210         } else {
2211             XtSetArg(args[0], XtNwidth, &w);
2212             XtGetValues(menuBarWidget, args, 1);
2213             w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2214         }
2215
2216         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2217         if (gres != XtGeometryYes && appData.debugMode) {
2218             fprintf(stderr,
2219                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2220                     programName, gres, w, h, wr, hr);
2221         }
2222     }
2223     XawFormDoLayout(formWidget, True);
2224
2225     xBoardWindow = XtWindow(boardWidget);
2226
2227     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2228     //       not need to go into InitDrawingSizes().
2229 #endif
2230
2231     /*
2232      * Create X checkmark bitmap and initialize option menu checks.
2233      */
2234     ReadBitmap(&xMarkPixmap, "checkmark.bm",
2235                checkmark_bits, checkmark_width, checkmark_height);
2236     XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2237     if (appData.alwaysPromoteToQueen) {
2238         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2239                     args, 1);
2240     }
2241     if (appData.animateDragging) {
2242         XtSetValues(XtNameToWidget(menuBarWidget,
2243                                    "menuOptions.Animate Dragging"),
2244                     args, 1);
2245     }
2246     if (appData.animate) {
2247         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2248                     args, 1);
2249     }
2250     if (appData.autoComment) {
2251         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
2252                     args, 1);
2253     }
2254     if (appData.autoCallFlag) {
2255         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2256                     args, 1);
2257     }
2258     if (appData.autoFlipView) {
2259         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2260                     args, 1);
2261     }
2262     if (appData.autoObserve) {
2263         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
2264                     args, 1);
2265     }
2266     if (appData.autoRaiseBoard) {
2267         XtSetValues(XtNameToWidget(menuBarWidget,
2268                                    "menuOptions.Auto Raise Board"), args, 1);
2269     }
2270     if (appData.autoSaveGames) {
2271         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2272                     args, 1);
2273     }
2274     if (appData.saveGameFile[0] != NULLCHAR) {
2275         /* Can't turn this off from menu */
2276         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2277                     args, 1);
2278         XtSetSensitive(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2279                        False);
2280
2281     }
2282     if (appData.blindfold) {
2283         XtSetValues(XtNameToWidget(menuBarWidget,
2284                                    "menuOptions.Blindfold"), args, 1);
2285     }
2286     if (appData.flashCount > 0) {
2287         XtSetValues(XtNameToWidget(menuBarWidget,
2288                                    "menuOptions.Flash Moves"),
2289                     args, 1);
2290     }
2291     if (appData.getMoveList) {
2292         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
2293                     args, 1);
2294     }
2295 #if HIGHDRAG
2296     if (appData.highlightDragging) {
2297         XtSetValues(XtNameToWidget(menuBarWidget,
2298                                    "menuOptions.Highlight Dragging"),
2299                     args, 1);
2300     }
2301 #endif
2302     if (appData.highlightLastMove) {
2303         XtSetValues(XtNameToWidget(menuBarWidget,
2304                                    "menuOptions.Highlight Last Move"),
2305                     args, 1);
2306     }
2307     if (appData.icsAlarm) {
2308         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2309                     args, 1);
2310     }
2311     if (appData.ringBellAfterMoves) {
2312         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2313                     args, 1);
2314     }
2315     if (appData.oldSaveStyle) {
2316         XtSetValues(XtNameToWidget(menuBarWidget,
2317                                    "menuOptions.Old Save Style"), args, 1);
2318     }
2319     if (appData.periodicUpdates) {
2320         XtSetValues(XtNameToWidget(menuBarWidget,
2321                                    "menuOptions.Periodic Updates"), args, 1);
2322     }
2323     if (appData.ponderNextMove) {
2324         XtSetValues(XtNameToWidget(menuBarWidget,
2325                                    "menuOptions.Ponder Next Move"), args, 1);
2326     }
2327     if (appData.popupExitMessage) {
2328         XtSetValues(XtNameToWidget(menuBarWidget,
2329                                    "menuOptions.Popup Exit Message"), args, 1);
2330     }
2331     if (appData.popupMoveErrors) {
2332         XtSetValues(XtNameToWidget(menuBarWidget,
2333                                    "menuOptions.Popup Move Errors"), args, 1);
2334     }
2335     if (appData.premove) {
2336         XtSetValues(XtNameToWidget(menuBarWidget,
2337                                    "menuOptions.Premove"), args, 1);
2338     }
2339     if (appData.quietPlay) {
2340         XtSetValues(XtNameToWidget(menuBarWidget,
2341                                    "menuOptions.Quiet Play"), args, 1);
2342     }
2343     if (appData.showCoords) {
2344         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2345                     args, 1);
2346     }
2347     if (appData.hideThinkingFromHuman) {
2348         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2349                     args, 1);
2350     }
2351     if (appData.testLegality) {
2352         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2353                     args, 1);
2354     }
2355     if (saveSettingsOnExit) {
2356         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2357                     args, 1);
2358     }
2359
2360     /*
2361      * Create an icon.
2362      */
2363     ReadBitmap(&wIconPixmap, "icon_white.bm",
2364                icon_white_bits, icon_white_width, icon_white_height);
2365     ReadBitmap(&bIconPixmap, "icon_black.bm",
2366                icon_black_bits, icon_black_width, icon_black_height);
2367     iconPixmap = wIconPixmap;
2368     i = 0;
2369     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
2370     XtSetValues(shellWidget, args, i);
2371
2372     /*
2373      * Create a cursor for the board widget.
2374      */
2375     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2376     XChangeWindowAttributes(xDisplay, xBoardWindow,
2377                             CWCursor, &window_attributes);
2378
2379     /*
2380      * Inhibit shell resizing.
2381      */
2382     shellArgs[0].value = (XtArgVal) &w;
2383     shellArgs[1].value = (XtArgVal) &h;
2384     XtGetValues(shellWidget, shellArgs, 2);
2385     shellArgs[4].value = shellArgs[2].value = w;
2386     shellArgs[5].value = shellArgs[3].value = h;
2387     XtSetValues(shellWidget, &shellArgs[2], 4);
2388     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2389     marginH =  h - boardHeight;
2390
2391     CatchDeleteWindow(shellWidget, "QuitProc");
2392
2393     CreateGCs();
2394     CreateGrid();
2395 #if HAVE_LIBXPM
2396     if (appData.bitmapDirectory[0] != NULLCHAR) {
2397       CreatePieces();
2398     } else {
2399       CreateXPMPieces();
2400     }
2401 #else
2402     CreateXIMPieces();
2403     /* Create regular pieces */
2404     if (!useImages) CreatePieces();
2405 #endif
2406
2407     CreatePieceMenus();
2408
2409     if (appData.animate || appData.animateDragging)
2410       CreateAnimVars();
2411
2412     XtAugmentTranslations(formWidget,
2413                           XtParseTranslationTable(globalTranslations));
2414     XtAugmentTranslations(boardWidget,
2415                           XtParseTranslationTable(boardTranslations));
2416     XtAugmentTranslations(whiteTimerWidget,
2417                           XtParseTranslationTable(whiteTranslations));
2418     XtAugmentTranslations(blackTimerWidget,
2419                           XtParseTranslationTable(blackTranslations));
2420
2421     /* Why is the following needed on some versions of X instead
2422      * of a translation? */
2423     XtAddEventHandler(boardWidget, ExposureMask, False,
2424                       (XtEventHandler) EventProc, NULL);
2425     /* end why */
2426
2427     /* [AS] Restore layout */
2428     if( wpMoveHistory.visible ) {
2429       HistoryPopUp();
2430     }
2431
2432     if( wpEvalGraph.visible ) 
2433       {
2434         EvalGraphPopUp();
2435       };
2436     
2437     if( wpEngineOutput.visible ) {
2438       EngineOutputPopUp();
2439     }
2440
2441     InitBackEnd2();
2442
2443     if (errorExitStatus == -1) {
2444         if (appData.icsActive) {
2445             /* We now wait until we see "login:" from the ICS before
2446                sending the logon script (problems with timestamp otherwise) */
2447             /*ICSInitScript();*/
2448             if (appData.icsInputBox) ICSInputBoxPopUp();
2449         }
2450
2451     #ifdef SIGWINCH
2452     signal(SIGWINCH, TermSizeSigHandler);
2453     #endif
2454         signal(SIGINT, IntSigHandler);
2455         signal(SIGTERM, IntSigHandler);
2456         if (*appData.cmailGameName != NULLCHAR) {
2457             signal(SIGUSR1, CmailSigHandler);
2458         }
2459     }
2460     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2461     InitPosition(TRUE);
2462
2463     XtAppMainLoop(appContext);
2464     if (appData.debugMode) fclose(debugFP); // [DM] debug
2465     return 0;
2466 }
2467
2468 void
2469 ShutDownFrontEnd()
2470 {
2471     if (appData.icsActive && oldICSInteractionTitle != NULL) {
2472         DisplayIcsInteractionTitle(oldICSInteractionTitle);
2473     }
2474     if (saveSettingsOnExit) SaveSettings(settingsFileName);
2475     unlink(gameCopyFilename);
2476     unlink(gamePasteFilename);
2477 }
2478
2479 RETSIGTYPE TermSizeSigHandler(int sig)
2480 {
2481     update_ics_width();
2482 }
2483
2484 RETSIGTYPE
2485 IntSigHandler(sig)
2486      int sig;
2487 {
2488     ExitEvent(sig);
2489 }
2490
2491 RETSIGTYPE
2492 CmailSigHandler(sig)
2493      int sig;
2494 {
2495     int dummy = 0;
2496     int error;
2497
2498     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
2499
2500     /* Activate call-back function CmailSigHandlerCallBack()             */
2501     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2502
2503     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2504 }
2505
2506 void
2507 CmailSigHandlerCallBack(isr, closure, message, count, error)
2508      InputSourceRef isr;
2509      VOIDSTAR closure;
2510      char *message;
2511      int count;
2512      int error;
2513 {
2514     BoardToTop();
2515     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
2516 }
2517 /**** end signal code ****/
2518
2519
2520 void
2521 ICSInitScript()
2522 {
2523     FILE *f;
2524     char buf[MSG_SIZ];
2525     char *p;
2526
2527     f = fopen(appData.icsLogon, "r");
2528     if (f == NULL) {
2529         p = getenv("HOME");
2530         if (p != NULL) {
2531             strcpy(buf, p);
2532             strcat(buf, "/");
2533             strcat(buf, appData.icsLogon);
2534             f = fopen(buf, "r");
2535         }
2536     }
2537     if (f != NULL)
2538       ProcessICSInitScript(f);
2539 }
2540
2541 void
2542 ResetFrontEnd()
2543 {
2544     CommentPopDown();
2545     EditCommentPopDown();
2546     TagsPopDown();
2547     return;
2548 }
2549
2550 typedef struct {
2551     char *name;
2552     Boolean value;
2553 } Enables;
2554
2555 void
2556 GreyRevert(grey)
2557      Boolean grey;
2558 {
2559     Widget w;
2560     if (!menuBarWidget) return;
2561     w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
2562     if (w == NULL) {
2563       DisplayError("menuStep.Revert", 0);
2564     } else {
2565       XtSetSensitive(w, !grey);
2566     }
2567 }
2568
2569 void
2570 SetMenuEnables(enab)
2571      Enables *enab;
2572 {
2573   Widget w;
2574   if (!menuBarWidget) return;
2575   while (enab->name != NULL) {
2576     w = XtNameToWidget(menuBarWidget, enab->name);
2577     if (w == NULL) {
2578       DisplayError(enab->name, 0);
2579     } else {
2580       XtSetSensitive(w, enab->value);
2581     }
2582     enab++;
2583   }
2584 }
2585
2586 Enables icsEnables[] = {
2587     { "menuFile.Mail Move", False },
2588     { "menuFile.Reload CMail Message", False },
2589     { "menuMode.Machine Black", False },
2590     { "menuMode.Machine White", False },
2591     { "menuMode.Analysis Mode", False },
2592     { "menuMode.Analyze File", False },
2593     { "menuMode.Two Machines", False },
2594 #ifndef ZIPPY
2595     { "menuHelp.Hint", False },
2596     { "menuHelp.Book", False },
2597     { "menuStep.Move Now", False },
2598     { "menuOptions.Periodic Updates", False },
2599     { "menuOptions.Hide Thinking", False },
2600     { "menuOptions.Ponder Next Move", False },
2601 #endif
2602     { NULL, False }
2603 };
2604
2605 Enables ncpEnables[] = {
2606     { "menuFile.Mail Move", False },
2607     { "menuFile.Reload CMail Message", False },
2608     { "menuMode.Machine White", False },
2609     { "menuMode.Machine Black", False },
2610     { "menuMode.Analysis Mode", False },
2611     { "menuMode.Analyze File", False },
2612     { "menuMode.Two Machines", False },
2613     { "menuMode.ICS Client", False },
2614     { "menuMode.ICS Input Box", False },
2615     { "Action", False },
2616     { "menuStep.Revert", False },
2617     { "menuStep.Move Now", False },
2618     { "menuStep.Retract Move", False },
2619     { "menuOptions.Auto Comment", False },
2620     { "menuOptions.Auto Flag", False },
2621     { "menuOptions.Auto Flip View", False },
2622     { "menuOptions.Auto Observe", False },
2623     { "menuOptions.Auto Raise Board", False },
2624     { "menuOptions.Get Move List", False },
2625     { "menuOptions.ICS Alarm", False },
2626     { "menuOptions.Move Sound", False },
2627     { "menuOptions.Quiet Play", False },
2628     { "menuOptions.Hide Thinking", False },
2629     { "menuOptions.Periodic Updates", False },
2630     { "menuOptions.Ponder Next Move", False },
2631     { "menuHelp.Hint", False },
2632     { "menuHelp.Book", False },
2633     { NULL, False }
2634 };
2635
2636 Enables gnuEnables[] = {
2637     { "menuMode.ICS Client", False },
2638     { "menuMode.ICS Input Box", False },
2639     { "menuAction.Accept", False },
2640     { "menuAction.Decline", False },
2641     { "menuAction.Rematch", False },
2642     { "menuAction.Adjourn", False },
2643     { "menuAction.Stop Examining", False },
2644     { "menuAction.Stop Observing", False },
2645     { "menuStep.Revert", False },
2646     { "menuOptions.Auto Comment", False },
2647     { "menuOptions.Auto Observe", False },
2648     { "menuOptions.Auto Raise Board", False },
2649     { "menuOptions.Get Move List", False },
2650     { "menuOptions.Premove", False },
2651     { "menuOptions.Quiet Play", False },
2652
2653     /* The next two options rely on SetCmailMode being called *after*    */
2654     /* SetGNUMode so that when GNU is being used to give hints these     */
2655     /* menu options are still available                                  */
2656
2657     { "menuFile.Mail Move", False },
2658     { "menuFile.Reload CMail Message", False },
2659     { NULL, False }
2660 };
2661
2662 Enables cmailEnables[] = {
2663     { "Action", True },
2664     { "menuAction.Call Flag", False },
2665     { "menuAction.Draw", True },
2666     { "menuAction.Adjourn", False },
2667     { "menuAction.Abort", False },
2668     { "menuAction.Stop Observing", False },
2669     { "menuAction.Stop Examining", False },
2670     { "menuFile.Mail Move", True },
2671     { "menuFile.Reload CMail Message", True },
2672     { NULL, False }
2673 };
2674
2675 Enables trainingOnEnables[] = {
2676   { "menuMode.Edit Comment", False },
2677   { "menuMode.Pause", False },
2678   { "menuStep.Forward", False },
2679   { "menuStep.Backward", False },
2680   { "menuStep.Forward to End", False },
2681   { "menuStep.Back to Start", False },
2682   { "menuStep.Move Now", False },
2683   { "menuStep.Truncate Game", False },
2684   { NULL, False }
2685 };
2686
2687 Enables trainingOffEnables[] = {
2688   { "menuMode.Edit Comment", True },
2689   { "menuMode.Pause", True },
2690   { "menuStep.Forward", True },
2691   { "menuStep.Backward", True },
2692   { "menuStep.Forward to End", True },
2693   { "menuStep.Back to Start", True },
2694   { "menuStep.Move Now", True },
2695   { "menuStep.Truncate Game", True },
2696   { NULL, False }
2697 };
2698
2699 Enables machineThinkingEnables[] = {
2700   { "menuFile.Load Game", False },
2701   { "menuFile.Load Next Game", False },
2702   { "menuFile.Load Previous Game", False },
2703   { "menuFile.Reload Same Game", False },
2704   { "menuFile.Paste Game", False },
2705   { "menuFile.Load Position", False },
2706   { "menuFile.Load Next Position", False },
2707   { "menuFile.Load Previous Position", False },
2708   { "menuFile.Reload Same Position", False },
2709   { "menuFile.Paste Position", False },
2710   { "menuMode.Machine White", False },
2711   { "menuMode.Machine Black", False },
2712   { "menuMode.Two Machines", False },
2713   { "menuStep.Retract Move", False },
2714   { NULL, False }
2715 };
2716
2717 Enables userThinkingEnables[] = {
2718   { "menuFile.Load Game", True },
2719   { "menuFile.Load Next Game", True },
2720   { "menuFile.Load Previous Game", True },
2721   { "menuFile.Reload Same Game", True },
2722   { "menuFile.Paste Game", True },
2723   { "menuFile.Load Position", True },
2724   { "menuFile.Load Next Position", True },
2725   { "menuFile.Load Previous Position", True },
2726   { "menuFile.Reload Same Position", True },
2727   { "menuFile.Paste Position", True },
2728   { "menuMode.Machine White", True },
2729   { "menuMode.Machine Black", True },
2730   { "menuMode.Two Machines", True },
2731   { "menuStep.Retract Move", True },
2732   { NULL, False }
2733 };
2734
2735 void SetICSMode()
2736 {
2737   SetMenuEnables(icsEnables);
2738
2739 #ifdef ZIPPY
2740   if (appData.zippyPlay && !appData.noChessProgram)   /* [DM] icsEngineAnalyze */
2741      XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2742 #endif
2743 }
2744
2745 void
2746 SetNCPMode()
2747 {
2748   SetMenuEnables(ncpEnables);
2749 }
2750
2751 void
2752 SetGNUMode()
2753 {
2754   SetMenuEnables(gnuEnables);
2755 }
2756
2757 void
2758 SetCmailMode()
2759 {
2760   SetMenuEnables(cmailEnables);
2761 }
2762
2763 void
2764 SetTrainingModeOn()
2765 {
2766   SetMenuEnables(trainingOnEnables);
2767   if (appData.showButtonBar) {
2768     XtSetSensitive(buttonBarWidget, False);
2769   }
2770   CommentPopDown();
2771 }
2772
2773 void
2774 SetTrainingModeOff()
2775 {
2776   SetMenuEnables(trainingOffEnables);
2777   if (appData.showButtonBar) {
2778     XtSetSensitive(buttonBarWidget, True);
2779   }
2780 }
2781
2782 void
2783 SetUserThinkingEnables()
2784 {
2785   if (appData.noChessProgram) return;
2786   SetMenuEnables(userThinkingEnables);
2787 }
2788
2789 void
2790 SetMachineThinkingEnables()
2791 {
2792   if (appData.noChessProgram) return;
2793   SetMenuEnables(machineThinkingEnables);
2794   switch (gameMode) {
2795   case MachinePlaysBlack:
2796   case MachinePlaysWhite:
2797   case TwoMachinesPlay:
2798     XtSetSensitive(XtNameToWidget(menuBarWidget,
2799                                   ModeToWidgetName(gameMode)), True);
2800     break;
2801   default:
2802     break;
2803   }
2804 }
2805
2806 #define Abs(n) ((n)<0 ? -(n) : (n))
2807
2808 /*
2809  * Find a font that matches "pattern" that is as close as
2810  * possible to the targetPxlSize.  Prefer fonts that are k
2811  * pixels smaller to fonts that are k pixels larger.  The
2812  * pattern must be in the X Consortium standard format,
2813  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2814  * The return value should be freed with XtFree when no
2815  * longer needed.
2816  */
2817 char *FindFont(pattern, targetPxlSize)
2818      char *pattern;
2819      int targetPxlSize;
2820 {
2821     char **fonts, *p, *best, *scalable, *scalableTail;
2822     int i, j, nfonts, minerr, err, pxlSize;
2823
2824 #ifdef ENABLE_NLS
2825     char **missing_list;
2826     int missing_count;
2827     char *def_string, *base_fnt_lst, strInt[3];
2828     XFontSet fntSet;
2829     XFontStruct **fnt_list;
2830
2831     base_fnt_lst = calloc(1, strlen(pattern) + 3);
2832     sprintf(strInt, "%d", targetPxlSize);
2833     p = strstr(pattern, "--");
2834     strncpy(base_fnt_lst, pattern, p - pattern + 2);
2835     strcat(base_fnt_lst, strInt);
2836     strcat(base_fnt_lst, strchr(p + 2, '-'));
2837
2838     if ((fntSet = XCreateFontSet(xDisplay,
2839                                  base_fnt_lst,
2840                                  &missing_list,
2841                                  &missing_count,
2842                                  &def_string)) == NULL) {
2843
2844        fprintf(stderr, _("Unable to create font set.\n"));
2845        exit (2);
2846     }
2847
2848     nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
2849 #else
2850     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2851     if (nfonts < 1) {
2852         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2853                 programName, pattern);
2854         exit(2);
2855     }
2856 #endif
2857
2858     best = fonts[0];
2859     scalable = NULL;
2860     minerr = 999999;
2861     for (i=0; i<nfonts; i++) {
2862         j = 0;
2863         p = fonts[i];
2864         if (*p != '-') continue;
2865         while (j < 7) {
2866             if (*p == NULLCHAR) break;
2867             if (*p++ == '-') j++;
2868         }
2869         if (j < 7) continue;
2870         pxlSize = atoi(p);
2871         if (pxlSize == 0) {
2872             scalable = fonts[i];
2873             scalableTail = p;
2874         } else {
2875             err = pxlSize - targetPxlSize;
2876             if (Abs(err) < Abs(minerr) ||
2877                 (minerr > 0 && err < 0 && -err == minerr)) {
2878                 best = fonts[i];
2879                 minerr = err;
2880             }
2881         }
2882     }
2883     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2884         /* If the error is too big and there is a scalable font,
2885            use the scalable font. */
2886         int headlen = scalableTail - scalable;
2887         p = (char *) XtMalloc(strlen(scalable) + 10);
2888         while (isdigit(*scalableTail)) scalableTail++;
2889         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2890     } else {
2891         p = (char *) XtMalloc(strlen(best) + 1);
2892         strcpy(p, best);
2893     }
2894     if (appData.debugMode) {
2895         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
2896                 pattern, targetPxlSize, p);
2897     }
2898 #ifdef ENABLE_NLS
2899     if (missing_count > 0)
2900        XFreeStringList(missing_list);
2901     XFreeFontSet(xDisplay, fntSet);
2902 #else
2903      XFreeFontNames(fonts);
2904 #endif
2905     return p;
2906 }
2907
2908 void CreateGCs()
2909 {
2910     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2911       | GCBackground | GCFunction | GCPlaneMask;
2912     XGCValues gc_values;
2913     GC copyInvertedGC;
2914
2915     gc_values.plane_mask = AllPlanes;
2916     gc_values.line_width = lineGap;
2917     gc_values.line_style = LineSolid;
2918     gc_values.function = GXcopy;
2919
2920     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2921     gc_values.background = XBlackPixel(xDisplay, xScreen);
2922     lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2923
2924     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2925     gc_values.background = XWhitePixel(xDisplay, xScreen);
2926     coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
2927     XSetFont(xDisplay, coordGC, coordFontID);
2928
2929     // [HGM] make font for holdings counts (white on black0
2930     gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2931     gc_values.background = XBlackPixel(xDisplay, xScreen);
2932     countGC = XtGetGC(shellWidget, value_mask, &gc_values);
2933     XSetFont(xDisplay, countGC, countFontID);
2934
2935     if (appData.monoMode) {
2936         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2937         gc_values.background = XWhitePixel(xDisplay, xScreen);
2938         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2939
2940         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2941         gc_values.background = XBlackPixel(xDisplay, xScreen);
2942         lightSquareGC = wbPieceGC
2943           = XtGetGC(shellWidget, value_mask, &gc_values);
2944
2945         gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2946         gc_values.background = XWhitePixel(xDisplay, xScreen);
2947         darkSquareGC = bwPieceGC
2948           = XtGetGC(shellWidget, value_mask, &gc_values);
2949
2950         if (DefaultDepth(xDisplay, xScreen) == 1) {
2951             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2952             gc_values.function = GXcopyInverted;
2953             copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
2954             gc_values.function = GXcopy;
2955             if (XBlackPixel(xDisplay, xScreen) == 1) {
2956                 bwPieceGC = darkSquareGC;
2957                 wbPieceGC = copyInvertedGC;
2958             } else {
2959                 bwPieceGC = copyInvertedGC;
2960                 wbPieceGC = lightSquareGC;
2961             }
2962         }
2963     } else {
2964         gc_values.foreground = highlightSquareColor;
2965         gc_values.background = highlightSquareColor;
2966         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2967
2968         gc_values.foreground = premoveHighlightColor;
2969         gc_values.background = premoveHighlightColor;
2970         prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2971
2972         gc_values.foreground = lightSquareColor;
2973         gc_values.background = darkSquareColor;
2974         lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2975
2976         gc_values.foreground = darkSquareColor;
2977         gc_values.background = lightSquareColor;
2978         darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2979
2980         gc_values.foreground = jailSquareColor;
2981         gc_values.background = jailSquareColor;
2982         jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2983
2984         gc_values.foreground = whitePieceColor;
2985         gc_values.background = darkSquareColor;
2986         wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2987
2988         gc_values.foreground = whitePieceColor;
2989         gc_values.background = lightSquareColor;
2990         wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2991
2992         gc_values.foreground = whitePieceColor;
2993         gc_values.background = jailSquareColor;
2994         wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2995
2996         gc_values.foreground = blackPieceColor;
2997         gc_values.background = darkSquareColor;
2998         bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2999
3000         gc_values.foreground = blackPieceColor;
3001         gc_values.background = lightSquareColor;
3002         blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3003
3004         gc_values.foreground = blackPieceColor;
3005         gc_values.background = jailSquareColor;
3006         bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3007     }
3008 }
3009
3010 void loadXIM(xim, xmask, filename, dest, mask)
3011      XImage *xim;
3012      XImage *xmask;
3013      char *filename;
3014      Pixmap *dest;
3015      Pixmap *mask;
3016 {
3017     int x, y, w, h, p;
3018     FILE *fp;
3019     Pixmap temp;
3020     XGCValues   values;
3021     GC maskGC;
3022
3023     fp = fopen(filename, "rb");
3024     if (!fp) {
3025         fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3026         exit(1);
3027     }
3028
3029     w = fgetc(fp);
3030     h = fgetc(fp);
3031
3032     for (y=0; y<h; ++y) {
3033         for (x=0; x<h; ++x) {
3034             p = fgetc(fp);
3035
3036             switch (p) {
3037               case 0:
3038                 XPutPixel(xim, x, y, blackPieceColor);
3039                 if (xmask)
3040                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3041                 break;
3042               case 1:
3043                 XPutPixel(xim, x, y, darkSquareColor);
3044                 if (xmask)
3045                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3046                 break;
3047               case 2:
3048                 XPutPixel(xim, x, y, whitePieceColor);
3049                 if (xmask)
3050                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3051                 break;
3052               case 3:
3053                 XPutPixel(xim, x, y, lightSquareColor);
3054                 if (xmask)
3055                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3056                 break;
3057             }
3058         }
3059     }
3060
3061     /* create Pixmap of piece */
3062     *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3063                           w, h, xim->depth);
3064     XPutImage(xDisplay, *dest, lightSquareGC, xim,
3065               0, 0, 0, 0, w, h);
3066
3067     /* create Pixmap of clipmask
3068        Note: We assume the white/black pieces have the same
3069              outline, so we make only 6 masks. This is okay
3070              since the XPM clipmask routines do the same. */
3071     if (xmask) {
3072       temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3073                             w, h, xim->depth);
3074       XPutImage(xDisplay, temp, lightSquareGC, xmask,
3075               0, 0, 0, 0, w, h);
3076
3077       /* now create the 1-bit version */
3078       *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3079                           w, h, 1);
3080
3081       values.foreground = 1;
3082       values.background = 0;
3083
3084       /* Don't use XtGetGC, not read only */
3085       maskGC = XCreateGC(xDisplay, *mask,
3086                     GCForeground | GCBackground, &values);
3087       XCopyPlane(xDisplay, temp, *mask, maskGC,
3088                   0, 0, squareSize, squareSize, 0, 0, 1);
3089       XFreePixmap(xDisplay, temp);
3090     }
3091 }
3092
3093
3094 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3095
3096 void CreateXIMPieces()
3097 {
3098     int piece, kind;
3099     char buf[MSG_SIZ];
3100     u_int ss;
3101     static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3102     XImage *ximtemp;
3103
3104     ss = squareSize;
3105
3106     /* The XSynchronize calls were copied from CreatePieces.
3107        Not sure if needed, but can't hurt */
3108     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3109                                      buffering bug */
3110
3111     /* temp needed by loadXIM() */
3112     ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3113                  0, 0, ss, ss, AllPlanes, XYPixmap);
3114
3115     if (strlen(appData.pixmapDirectory) == 0) {
3116       useImages = 0;
3117     } else {
3118         useImages = 1;
3119         if (appData.monoMode) {
3120           DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3121                             0, 2);
3122           ExitEvent(2);
3123         }
3124         fprintf(stderr, _("\nLoading XIMs...\n"));
3125         /* Load pieces */
3126         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3127             fprintf(stderr, "%d", piece+1);
3128             for (kind=0; kind<4; kind++) {
3129                 fprintf(stderr, ".");
3130                 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3131                         ExpandPathName(appData.pixmapDirectory),
3132                         piece <= (int) WhiteKing ? "" : "w",
3133                         pieceBitmapNames[piece],
3134                         ximkind[kind], ss);
3135                 ximPieceBitmap[kind][piece] =
3136                   XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3137                             0, 0, ss, ss, AllPlanes, XYPixmap);
3138                 if (appData.debugMode)
3139                   fprintf(stderr, _("(File:%s:) "), buf);
3140                 loadXIM(ximPieceBitmap[kind][piece],
3141                         ximtemp, buf,
3142                         &(xpmPieceBitmap2[kind][piece]),
3143                         &(ximMaskPm2[piece]));
3144                 if(piece <= (int)WhiteKing)
3145                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3146             }
3147             fprintf(stderr," ");
3148         }
3149         /* Load light and dark squares */
3150         /* If the LSQ and DSQ pieces don't exist, we will
3151            draw them with solid squares. */
3152         snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3153         if (access(buf, 0) != 0) {
3154             useImageSqs = 0;
3155         } else {
3156             useImageSqs = 1;
3157             fprintf(stderr, _("light square "));
3158             ximLightSquare=
3159               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3160                         0, 0, ss, ss, AllPlanes, XYPixmap);
3161             if (appData.debugMode)
3162               fprintf(stderr, _("(File:%s:) "), buf);
3163
3164             loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3165             fprintf(stderr, _("dark square "));
3166             snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3167                     ExpandPathName(appData.pixmapDirectory), ss);
3168             if (appData.debugMode)
3169               fprintf(stderr, _("(File:%s:) "), buf);
3170             ximDarkSquare=
3171               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3172                         0, 0, ss, ss, AllPlanes, XYPixmap);
3173             loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3174             xpmJailSquare = xpmLightSquare;
3175         }
3176         fprintf(stderr, _("Done.\n"));
3177     }
3178     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3179 }
3180
3181 #if HAVE_LIBXPM
3182 void CreateXPMPieces()
3183 {
3184     int piece, kind, r;
3185     char buf[MSG_SIZ];
3186     u_int ss = squareSize;
3187     XpmAttributes attr;
3188     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3189     XpmColorSymbol symbols[4];
3190
3191     /* The XSynchronize calls were copied from CreatePieces.
3192        Not sure if needed, but can't hurt */
3193     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3194
3195     /* Setup translations so piece colors match square colors */
3196     symbols[0].name = "light_piece";
3197     symbols[0].value = appData.whitePieceColor;
3198     symbols[1].name = "dark_piece";
3199     symbols[1].value = appData.blackPieceColor;
3200     symbols[2].name = "light_square";
3201     symbols[2].value = appData.lightSquareColor;
3202     symbols[3].name = "dark_square";
3203     symbols[3].value = appData.darkSquareColor;
3204
3205     attr.valuemask = XpmColorSymbols;
3206     attr.colorsymbols = symbols;
3207     attr.numsymbols = 4;
3208
3209     if (appData.monoMode) {
3210       DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3211                         0, 2);
3212       ExitEvent(2);
3213     }
3214     if (strlen(appData.pixmapDirectory) == 0) {
3215         XpmPieces* pieces = builtInXpms;
3216         useImages = 1;
3217         /* Load pieces */
3218         while (pieces->size != squareSize && pieces->size) pieces++;
3219         if (!pieces->size) {
3220           fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3221           exit(1);
3222         }
3223         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3224             for (kind=0; kind<4; kind++) {
3225
3226                 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3227                                                pieces->xpm[piece][kind],
3228                                                &(xpmPieceBitmap2[kind][piece]),
3229                                                NULL, &attr)) != 0) {
3230                   fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3231                           r, buf);
3232                   exit(1);
3233                 }
3234                 if(piece <= (int) WhiteKing)
3235                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3236             }
3237         }
3238         useImageSqs = 0;
3239         xpmJailSquare = xpmLightSquare;
3240     } else {
3241         useImages = 1;
3242
3243         fprintf(stderr, _("\nLoading XPMs...\n"));
3244
3245         /* Load pieces */
3246         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3247             fprintf(stderr, "%d ", piece+1);
3248             for (kind=0; kind<4; kind++) {
3249               snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3250                         ExpandPathName(appData.pixmapDirectory),
3251                         piece > (int) WhiteKing ? "w" : "",
3252                         pieceBitmapNames[piece],
3253                         xpmkind[kind], ss);
3254                 if (appData.debugMode) {
3255                     fprintf(stderr, _("(File:%s:) "), buf);
3256                 }
3257                 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3258                                            &(xpmPieceBitmap2[kind][piece]),
3259                                            NULL, &attr)) != 0) {
3260                     if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3261                       // [HGM] missing: read of unorthodox piece failed; substitute King.
3262                       snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3263                                 ExpandPathName(appData.pixmapDirectory),
3264                                 xpmkind[kind], ss);
3265                         if (appData.debugMode) {
3266                             fprintf(stderr, _("(Replace by File:%s:) "), buf);
3267                         }
3268                         r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3269                                                 &(xpmPieceBitmap2[kind][piece]),
3270                                                 NULL, &attr);
3271                     }
3272                     if (r != 0) {
3273                         fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3274                                 r, buf);
3275                         exit(1);
3276                     }
3277                 }
3278                 if(piece <= (int) WhiteKing) 
3279                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3280             }
3281         }
3282         /* Load light and dark squares */
3283         /* If the LSQ and DSQ pieces don't exist, we will
3284            draw them with solid squares. */
3285         fprintf(stderr, _("light square "));
3286         snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3287         if (access(buf, 0) != 0) {
3288             useImageSqs = 0;
3289         } else {
3290             useImageSqs = 1;
3291             if (appData.debugMode)
3292               fprintf(stderr, _("(File:%s:) "), buf);
3293
3294             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3295                                        &xpmLightSquare, NULL, &attr)) != 0) {
3296                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3297                 exit(1);
3298             }
3299             fprintf(stderr, _("dark square "));
3300             snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3301                     ExpandPathName(appData.pixmapDirectory), ss);
3302             if (appData.debugMode) {
3303                 fprintf(stderr, _("(File:%s:) "), buf);
3304             }
3305             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3306                                        &xpmDarkSquare, NULL, &attr)) != 0) {
3307                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3308                 exit(1);
3309             }
3310         }
3311         xpmJailSquare = xpmLightSquare;
3312         fprintf(stderr, _("Done.\n"));
3313     }
3314     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3315                                       buffering bug */
3316 }
3317 #endif /* HAVE_LIBXPM */
3318
3319 #if HAVE_LIBXPM
3320 /* No built-in bitmaps */
3321 void CreatePieces()
3322 {
3323     int piece, kind;
3324     char buf[MSG_SIZ];
3325     u_int ss = squareSize;
3326
3327     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3328                                      buffering bug */
3329
3330     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3331         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3332             sprintf(buf, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3333                     pieceBitmapNames[piece],
3334                     ss, kind == SOLID ? 's' : 'o');
3335             ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3336             if(piece <= (int)WhiteKing)
3337                 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3338         }
3339     }
3340
3341     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3342                                       buffering bug */
3343 }
3344 #else
3345 /* With built-in bitmaps */
3346 void CreatePieces()
3347 {
3348     BuiltInBits* bib = builtInBits;
3349     int piece, kind;
3350     char buf[MSG_SIZ];
3351     u_int ss = squareSize;
3352
3353     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3354                                      buffering bug */
3355
3356     while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3357
3358     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3359         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3360             sprintf(buf, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3361                     pieceBitmapNames[piece],
3362                     ss, kind == SOLID ? 's' : 'o');
3363             ReadBitmap(&pieceBitmap2[kind][piece], buf,
3364                        bib->bits[kind][piece], ss, ss);
3365             if(piece <= (int)WhiteKing)
3366                 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3367         }
3368     }
3369
3370     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3371                                       buffering bug */
3372 }
3373 #endif
3374
3375 void ReadBitmap(pm, name, bits, wreq, hreq)
3376      Pixmap *pm;
3377      String name;
3378      unsigned char bits[];
3379      u_int wreq, hreq;
3380 {
3381     int x_hot, y_hot;
3382     u_int w, h;
3383     int errcode;
3384     char msg[MSG_SIZ], fullname[MSG_SIZ];
3385
3386     if (*appData.bitmapDirectory != NULLCHAR) {
3387         strcpy(fullname, appData.bitmapDirectory);
3388         strcat(fullname, "/");
3389         strcat(fullname, name);
3390         errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3391                                   &w, &h, pm, &x_hot, &y_hot);
3392     fprintf(stderr, "load %s\n", name);
3393         if (errcode != BitmapSuccess) {
3394             switch (errcode) {
3395               case BitmapOpenFailed:
3396                 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3397                 break;
3398               case BitmapFileInvalid:
3399                 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3400                 break;
3401               case BitmapNoMemory:
3402                 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3403                         fullname);
3404                 break;
3405               default:
3406                 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3407                         errcode, fullname);
3408                 break;
3409             }
3410             fprintf(stderr, _("%s: %s...using built-in\n"),
3411                     programName, msg);
3412         } else if (w != wreq || h != hreq) {
3413             fprintf(stderr,
3414                     _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3415                     programName, fullname, w, h, wreq, hreq);
3416         } else {
3417             return;
3418         }
3419     }
3420     if (bits != NULL) {
3421         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3422                                     wreq, hreq);
3423     }
3424 }
3425
3426 void CreateGrid()
3427 {
3428     int i, j;
3429
3430     if (lineGap == 0) return;
3431
3432     /* [HR] Split this into 2 loops for non-square boards. */
3433
3434     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3435         gridSegments[i].x1 = 0;
3436         gridSegments[i].x2 =
3437           lineGap + BOARD_WIDTH * (squareSize + lineGap);
3438         gridSegments[i].y1 = gridSegments[i].y2
3439           = lineGap / 2 + (i * (squareSize + lineGap));
3440     }
3441
3442     for (j = 0; j < BOARD_WIDTH + 1; j++) {
3443         gridSegments[j + i].y1 = 0;
3444         gridSegments[j + i].y2 =
3445           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3446         gridSegments[j + i].x1 = gridSegments[j + i].x2
3447           = lineGap / 2 + (j * (squareSize + lineGap));
3448     }
3449 }
3450
3451 static void MenuBarSelect(w, addr, index)
3452      Widget w;
3453      caddr_t addr;
3454      caddr_t index;
3455 {
3456     XtActionProc proc = (XtActionProc) addr;
3457
3458     (proc)(NULL, NULL, NULL, NULL);
3459 }
3460
3461 void CreateMenuBarPopup(parent, name, mb)
3462      Widget parent;
3463      String name;
3464      Menu *mb;
3465 {
3466     int j;
3467     Widget menu, entry;
3468     MenuItem *mi;
3469     Arg args[16];
3470
3471     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3472                               parent, NULL, 0);
3473     j = 0;
3474     XtSetArg(args[j], XtNleftMargin, 20);   j++;
3475     XtSetArg(args[j], XtNrightMargin, 20);  j++;
3476     mi = mb->mi;
3477     while (mi->string != NULL) {
3478         if (strcmp(mi->string, "----") == 0) {
3479             entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3480                                           menu, args, j);
3481         } else {
3482           XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3483             entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
3484                                           menu, args, j+1);
3485             XtAddCallback(entry, XtNcallback,
3486                           (XtCallbackProc) MenuBarSelect,
3487                           (caddr_t) mi->proc);
3488         }
3489         mi++;
3490     }
3491 }
3492
3493 Widget CreateMenuBar(mb)
3494      Menu *mb;
3495 {
3496     int j;
3497     Widget anchor, menuBar;
3498     Arg args[16];
3499     char menuName[MSG_SIZ];
3500
3501     j = 0;
3502     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
3503     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3504     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3505     menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3506                              formWidget, args, j);
3507
3508     while (mb->name != NULL) {
3509         strcpy(menuName, "menu");
3510         strcat(menuName, mb->name);
3511         j = 0;
3512         XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
3513         if (tinyLayout) {
3514             char shortName[2];
3515             shortName[0] = _(mb->name)[0];
3516             shortName[1] = NULLCHAR;
3517             XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3518         }
3519       else {
3520           XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3521       }
3522
3523         XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3524         anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3525                                        menuBar, args, j);
3526         CreateMenuBarPopup(menuBar, menuName, mb);
3527         mb++;
3528     }
3529     return menuBar;
3530 }
3531
3532 Widget CreateButtonBar(mi)
3533      MenuItem *mi;
3534 {
3535     int j;
3536     Widget button, buttonBar;
3537     Arg args[16];
3538
3539     j = 0;
3540     XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3541     if (tinyLayout) {
3542         XtSetArg(args[j], XtNhSpace, 0); j++;
3543     }
3544     XtSetArg(args[j], XtNborderWidth, 0); j++;
3545     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3546     buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3547                                formWidget, args, j);
3548
3549     while (mi->string != NULL) {
3550         j = 0;
3551         if (tinyLayout) {
3552             XtSetArg(args[j], XtNinternalWidth, 2); j++;
3553             XtSetArg(args[j], XtNborderWidth, 0); j++;
3554         }
3555       XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3556         button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3557                                        buttonBar, args, j);
3558         XtAddCallback(button, XtNcallback,
3559                       (XtCallbackProc) MenuBarSelect,
3560                       (caddr_t) mi->proc);
3561         mi++;
3562     }
3563     return buttonBar;
3564 }
3565
3566 Widget
3567 CreatePieceMenu(name, color)
3568      char *name;
3569      int color;
3570 {
3571     int i;
3572     Widget entry, menu;
3573     Arg args[16];
3574     ChessSquare selection;
3575
3576     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3577                               boardWidget, args, 0);
3578
3579     for (i = 0; i < PIECE_MENU_SIZE; i++) {
3580         String item = pieceMenuStrings[color][i];
3581
3582         if (strcmp(item, "----") == 0) {
3583             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3584                                           menu, NULL, 0);
3585         } else {
3586           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3587             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3588                                 menu, args, 1);
3589             selection = pieceMenuTranslation[color][i];
3590             XtAddCallback(entry, XtNcallback,
3591                           (XtCallbackProc) PieceMenuSelect,
3592                           (caddr_t) selection);
3593             if (selection == WhitePawn || selection == BlackPawn) {
3594                 XtSetArg(args[0], XtNpopupOnEntry, entry);
3595                 XtSetValues(menu, args, 1);
3596             }
3597         }
3598     }
3599     return menu;
3600 }
3601
3602 void
3603 CreatePieceMenus()
3604 {
3605     int i;
3606     Widget entry;
3607     Arg args[16];
3608     ChessSquare selection;
3609
3610     whitePieceMenu = CreatePieceMenu("menuW", 0);
3611     blackPieceMenu = CreatePieceMenu("menuB", 1);
3612
3613     XtRegisterGrabAction(PieceMenuPopup, True,
3614                          (unsigned)(ButtonPressMask|ButtonReleaseMask),
3615                          GrabModeAsync, GrabModeAsync);
3616
3617     XtSetArg(args[0], XtNlabel, _("Drop"));
3618     dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3619                                   boardWidget, args, 1);
3620     for (i = 0; i < DROP_MENU_SIZE; i++) {
3621         String item = dropMenuStrings[i];
3622
3623         if (strcmp(item, "----") == 0) {
3624             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3625                                           dropMenu, NULL, 0);
3626         } else {
3627           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3628             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3629                                 dropMenu, args, 1);
3630             selection = dropMenuTranslation[i];
3631             XtAddCallback(entry, XtNcallback,
3632                           (XtCallbackProc) DropMenuSelect,
3633                           (caddr_t) selection);
3634         }
3635     }
3636 }
3637
3638 void SetupDropMenu()
3639 {
3640     int i, j, count;
3641     char label[32];
3642     Arg args[16];
3643     Widget entry;
3644     char* p;
3645
3646     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3647         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3648         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3649                    dmEnables[i].piece);
3650         XtSetSensitive(entry, p != NULL || !appData.testLegality
3651                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3652                                        && !appData.icsActive));
3653         count = 0;
3654         while (p && *p++ == dmEnables[i].piece) count++;
3655         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
3656         j = 0;
3657         XtSetArg(args[j], XtNlabel, label); j++;
3658         XtSetValues(entry, args, j);
3659     }
3660 }
3661
3662 void PieceMenuPopup(w, event, params, num_params)
3663      Widget w;
3664      XEvent *event;
3665      String *params;
3666      Cardinal *num_params;
3667 {
3668     String whichMenu;
3669     if (event->type != ButtonPress) return;
3670     if (errorUp) ErrorPopDown();
3671     switch (gameMode) {
3672       case EditPosition:
3673       case IcsExamining:
3674         whichMenu = params[0];
3675         break;
3676       case IcsPlayingWhite:
3677       case IcsPlayingBlack:
3678       case EditGame:
3679       case MachinePlaysWhite:
3680       case MachinePlaysBlack:
3681         if (appData.testLegality &&
3682             gameInfo.variant != VariantBughouse &&
3683             gameInfo.variant != VariantCrazyhouse) return;
3684         SetupDropMenu();
3685         whichMenu = "menuD";
3686         break;
3687       default:
3688         return;
3689     }
3690
3691     if (((pmFromX = EventToSquare(event->xbutton.x, BOARD_WIDTH)) < 0) ||
3692         ((pmFromY = EventToSquare(event->xbutton.y, BOARD_HEIGHT)) < 0)) {
3693         pmFromX = pmFromY = -1;
3694         return;
3695     }
3696     if (flipView)
3697       pmFromX = BOARD_WIDTH - 1 - pmFromX;
3698     else
3699       pmFromY = BOARD_HEIGHT - 1 - pmFromY;
3700
3701     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3702 }
3703
3704 static void PieceMenuSelect(w, piece, junk)
3705      Widget w;
3706      ChessSquare piece;
3707      caddr_t junk;
3708 {
3709     if (pmFromX < 0 || pmFromY < 0) return;
3710     EditPositionMenuEvent(piece, pmFromX, pmFromY);
3711 }
3712
3713 static void DropMenuSelect(w, piece, junk)
3714      Widget w;
3715      ChessSquare piece;
3716      caddr_t junk;
3717 {
3718     if (pmFromX < 0 || pmFromY < 0) return;
3719     DropMenuEvent(piece, pmFromX, pmFromY);
3720 }
3721
3722 void WhiteClock(w, event, prms, nprms)
3723      Widget w;
3724      XEvent *event;
3725      String *prms;
3726      Cardinal *nprms;
3727 {
3728     if (gameMode == EditPosition || gameMode == IcsExamining) {
3729         SetWhiteToPlayEvent();
3730     } else if (gameMode == IcsPlayingBlack || gameMode == MachinePlaysWhite) {
3731         CallFlagEvent();
3732     }
3733 }
3734
3735 void BlackClock(w, event, prms, nprms)
3736      Widget w;
3737      XEvent *event;
3738      String *prms;
3739      Cardinal *nprms;
3740 {
3741     if (gameMode == EditPosition || gameMode == IcsExamining) {
3742         SetBlackToPlayEvent();
3743     } else if (gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) {
3744         CallFlagEvent();
3745     }
3746 }
3747
3748
3749 /*
3750  * If the user selects on a border boundary, return -1; if off the board,
3751  *   return -2.  Otherwise map the event coordinate to the square.
3752  */
3753 int EventToSquare(x, limit)
3754      int x;
3755 {
3756     if (x <= 0)
3757       return -2;
3758     if (x < lineGap)
3759       return -1;
3760     x -= lineGap;
3761     if ((x % (squareSize + lineGap)) >= squareSize)
3762       return -1;
3763     x /= (squareSize + lineGap);
3764     if (x >= limit)
3765       return -2;
3766     return x;
3767 }
3768
3769 static void do_flash_delay(msec)
3770      unsigned long msec;
3771 {
3772     TimeDelay(msec);
3773 }
3774
3775 static void drawHighlight(file, rank, gc)
3776      int file, rank;
3777      GC gc;
3778 {
3779     int x, y;
3780
3781     if (lineGap == 0 || appData.blindfold) return;
3782
3783     if (flipView) {
3784         x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
3785           (squareSize + lineGap);
3786         y = lineGap/2 + rank * (squareSize + lineGap);
3787     } else {
3788         x = lineGap/2 + file * (squareSize + lineGap);
3789         y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
3790           (squareSize + lineGap);
3791     }
3792
3793     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3794                    squareSize+lineGap, squareSize+lineGap);
3795 }
3796
3797 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
3798 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
3799
3800 void
3801 SetHighlights(fromX, fromY, toX, toY)
3802      int fromX, fromY, toX, toY;
3803 {
3804     if (hi1X != fromX || hi1Y != fromY) {
3805         if (hi1X >= 0 && hi1Y >= 0) {
3806             drawHighlight(hi1X, hi1Y, lineGC);
3807         }
3808         if (fromX >= 0 && fromY >= 0) {
3809             drawHighlight(fromX, fromY, highlineGC);
3810         }
3811     }
3812     if (hi2X != toX || hi2Y != toY) {
3813         if (hi2X >= 0 && hi2Y >= 0) {
3814             drawHighlight(hi2X, hi2Y, lineGC);
3815         }
3816         if (toX >= 0 && toY >= 0) {
3817             drawHighlight(toX, toY, highlineGC);
3818         }
3819     }
3820     hi1X = fromX;
3821     hi1Y = fromY;
3822     hi2X = toX;
3823     hi2Y = toY;
3824 }
3825
3826 void
3827 ClearHighlights()
3828 {
3829     SetHighlights(-1, -1, -1, -1);
3830 }
3831
3832
3833 void
3834 SetPremoveHighlights(fromX, fromY, toX, toY)
3835      int fromX, fromY, toX, toY;
3836 {
3837     if (pm1X != fromX || pm1Y != fromY) {
3838         if (pm1X >= 0 && pm1Y >= 0) {
3839             drawHighlight(pm1X, pm1Y, lineGC);
3840         }
3841         if (fromX >= 0 && fromY >= 0) {
3842             drawHighlight(fromX, fromY, prelineGC);
3843         }
3844     }
3845     if (pm2X != toX || pm2Y != toY) {
3846         if (pm2X >= 0 && pm2Y >= 0) {
3847             drawHighlight(pm2X, pm2Y, lineGC);
3848         }
3849         if (toX >= 0 && toY >= 0) {
3850             drawHighlight(toX, toY, prelineGC);
3851         }
3852     }
3853     pm1X = fromX;
3854     pm1Y = fromY;
3855     pm2X = toX;
3856     pm2Y = toY;
3857 }
3858
3859 void
3860 ClearPremoveHighlights()
3861 {
3862   SetPremoveHighlights(-1, -1, -1, -1);
3863 }
3864
3865 static void BlankSquare(x, y, color, piece, dest)
3866      int x, y, color;
3867      ChessSquare piece;
3868      Drawable dest;
3869 {
3870     if (useImages && useImageSqs) {
3871         Pixmap pm;
3872         switch (color) {
3873           case 1: /* light */
3874             pm = xpmLightSquare;
3875             break;
3876           case 0: /* dark */
3877             pm = xpmDarkSquare;
3878             break;
3879           case 2: /* neutral */
3880           default:
3881             pm = xpmJailSquare;
3882             break;
3883         }
3884         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3885                   squareSize, squareSize, x, y);
3886     } else {
3887         GC gc;
3888         switch (color) {
3889           case 1: /* light */
3890             gc = lightSquareGC;
3891             break;
3892           case 0: /* dark */
3893             gc = darkSquareGC;
3894             break;
3895           case 2: /* neutral */
3896           default:
3897             gc = jailSquareGC;
3898             break;
3899         }
3900         XFillRectangle(xDisplay, dest, gc, x, y, squareSize, squareSize);
3901     }
3902 }
3903
3904 /*
3905    I split out the routines to draw a piece so that I could
3906    make a generic flash routine.
3907 */
3908 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
3909      ChessSquare piece;
3910      int square_color, x, y;
3911      Drawable dest;
3912 {
3913     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3914     switch (square_color) {
3915       case 1: /* light */
3916       case 2: /* neutral */
3917       default:
3918         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3919                   ? *pieceToOutline(piece)
3920                   : *pieceToSolid(piece),
3921                   dest, bwPieceGC, 0, 0,
3922                   squareSize, squareSize, x, y);
3923         break;
3924       case 0: /* dark */
3925         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3926                   ? *pieceToSolid(piece)
3927                   : *pieceToOutline(piece),
3928                   dest, wbPieceGC, 0, 0,
3929                   squareSize, squareSize, x, y);
3930         break;
3931     }
3932 }
3933
3934 static void monoDrawPiece(piece, square_color, x, y, dest)
3935      ChessSquare piece;
3936      int square_color, x, y;
3937      Drawable dest;
3938 {
3939     switch (square_color) {
3940       case 1: /* light */
3941       case 2: /* neutral */
3942       default:
3943         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3944                    ? *pieceToOutline(piece)
3945                    : *pieceToSolid(piece),
3946                    dest, bwPieceGC, 0, 0,
3947                    squareSize, squareSize, x, y, 1);
3948         break;
3949       case 0: /* dark */
3950         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3951                    ? *pieceToSolid(piece)
3952                    : *pieceToOutline(piece),
3953                    dest, wbPieceGC, 0, 0,
3954                    squareSize, squareSize, x, y, 1);
3955         break;
3956     }
3957 }
3958
3959 static void colorDrawPiece(piece, square_color, x, y, dest)
3960      ChessSquare piece;
3961      int square_color, x, y;
3962      Drawable dest;
3963 {
3964     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3965     switch (square_color) {
3966       case 1: /* light */
3967         XCopyPlane(xDisplay, *pieceToSolid(piece),
3968                    dest, (int) piece < (int) BlackPawn
3969                    ? wlPieceGC : blPieceGC, 0, 0,
3970                    squareSize, squareSize, x, y, 1);
3971         break;
3972       case 0: /* dark */
3973         XCopyPlane(xDisplay, *pieceToSolid(piece),
3974                    dest, (int) piece < (int) BlackPawn
3975                    ? wdPieceGC : bdPieceGC, 0, 0,
3976                    squareSize, squareSize, x, y, 1);
3977         break;
3978       case 2: /* neutral */
3979       default:
3980         XCopyPlane(xDisplay, *pieceToSolid(piece),
3981                    dest, (int) piece < (int) BlackPawn
3982                    ? wjPieceGC : bjPieceGC, 0, 0,
3983                    squareSize, squareSize, x, y, 1);
3984         break;
3985     }
3986 }
3987
3988 static void colorDrawPieceImage(piece, square_color, x, y, dest)
3989      ChessSquare piece;
3990      int square_color, x, y;
3991      Drawable dest;
3992 {
3993     int kind;
3994
3995     switch (square_color) {
3996       case 1: /* light */
3997       case 2: /* neutral */
3998       default:
3999         if ((int)piece < (int) BlackPawn) {
4000             kind = 0;
4001         } else {
4002             kind = 2;
4003             piece -= BlackPawn;
4004         }
4005         break;
4006       case 0: /* dark */
4007         if ((int)piece < (int) BlackPawn) {
4008             kind = 1;
4009         } else {
4010             kind = 3;
4011             piece -= BlackPawn;
4012         }
4013         break;
4014     }
4015     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4016               dest, wlPieceGC, 0, 0,
4017               squareSize, squareSize, x, y);
4018 }
4019
4020 typedef void (*DrawFunc)();
4021
4022 DrawFunc ChooseDrawFunc()
4023 {
4024     if (appData.monoMode) {
4025         if (DefaultDepth(xDisplay, xScreen) == 1) {
4026             return monoDrawPiece_1bit;
4027         } else {
4028             return monoDrawPiece;
4029         }
4030     } else {
4031         if (useImages)
4032           return colorDrawPieceImage;
4033         else
4034           return colorDrawPiece;
4035     }
4036 }
4037
4038 /* [HR] determine square color depending on chess variant. */
4039 static int SquareColor(row, column)
4040      int row, column;
4041 {
4042     int square_color;
4043
4044     if (gameInfo.variant == VariantXiangqi) {
4045         if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4046             square_color = 1;
4047         } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4048             square_color = 0;
4049         } else if (row <= 4) {
4050             square_color = 0;
4051         } else {
4052             square_color = 1;
4053         }
4054     } else {
4055         square_color = ((column + row) % 2) == 1;
4056     }
4057
4058     /* [hgm] holdings: next line makes all holdings squares light */
4059     if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4060
4061     return square_color;
4062 }
4063
4064 void DrawSquare(row, column, piece, do_flash)
4065      int row, column, do_flash;
4066      ChessSquare piece;
4067 {
4068     int square_color, x, y, direction, font_ascent, font_descent;
4069     int i;
4070     char string[2];
4071     XCharStruct overall;
4072     DrawFunc drawfunc;
4073     int flash_delay;
4074
4075     /* Calculate delay in milliseconds (2-delays per complete flash) */
4076     flash_delay = 500 / appData.flashRate;
4077
4078     if (flipView) {
4079         x = lineGap + ((BOARD_WIDTH-1)-column) *
4080           (squareSize + lineGap);
4081         y = lineGap + row * (squareSize + lineGap);
4082     } else {
4083         x = lineGap + column * (squareSize + lineGap);
4084         y = lineGap + ((BOARD_HEIGHT-1)-row) *
4085           (squareSize + lineGap);
4086     }
4087
4088     square_color = SquareColor(row, column);
4089
4090     if ( // [HGM] holdings: blank out area between board and holdings
4091                  column == BOARD_LEFT-1 ||  column == BOARD_RGHT
4092               || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4093                   || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4094                         BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
4095
4096                         // [HGM] print piece counts next to holdings
4097                         string[1] = NULLCHAR;
4098                         if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4099                             string[0] = '0' + piece;
4100                             XTextExtents(countFontStruct, string, 1, &direction,
4101                                  &font_ascent, &font_descent, &overall);
4102                             if (appData.monoMode) {
4103                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4104                                                  x + squareSize - overall.width - 2,
4105                                                  y + font_ascent + 1, string, 1);
4106                             } else {
4107                                 XDrawString(xDisplay, xBoardWindow, countGC,
4108                                             x + squareSize - overall.width - 2,
4109                                             y + font_ascent + 1, string, 1);
4110                             }
4111                         }
4112                         if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4113                             string[0] = '0' + piece;
4114                             XTextExtents(countFontStruct, string, 1, &direction,
4115                                          &font_ascent, &font_descent, &overall);
4116                             if (appData.monoMode) {
4117                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4118                                                  x + 2, y + font_ascent + 1, string, 1);
4119                             } else {
4120                                 XDrawString(xDisplay, xBoardWindow, countGC,
4121                                             x + 2, y + font_ascent + 1, string, 1);
4122                             }
4123                         }
4124     } else {
4125             if (piece == EmptySquare || appData.blindfold) {
4126                         BlankSquare(x, y, square_color, piece, xBoardWindow);
4127             } else {
4128                         drawfunc = ChooseDrawFunc();
4129                         if (do_flash && appData.flashCount > 0) {
4130                             for (i=0; i<appData.flashCount; ++i) {
4131
4132                                         drawfunc(piece, square_color, x, y, xBoardWindow);
4133                                         XSync(xDisplay, False);
4134                                         do_flash_delay(flash_delay);
4135
4136                                         BlankSquare(x, y, square_color, piece, xBoardWindow);
4137                                         XSync(xDisplay, False);
4138                                         do_flash_delay(flash_delay);
4139                             }
4140                         }
4141                         drawfunc(piece, square_color, x, y, xBoardWindow);
4142         }
4143         }
4144
4145     string[1] = NULLCHAR;
4146     if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4147                 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4148         string[0] = 'a' + column - BOARD_LEFT;
4149         XTextExtents(coordFontStruct, string, 1, &direction,
4150                      &font_ascent, &font_descent, &overall);
4151         if (appData.monoMode) {
4152             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4153                              x + squareSize - overall.width - 2,
4154                              y + squareSize - font_descent - 1, string, 1);
4155         } else {
4156             XDrawString(xDisplay, xBoardWindow, coordGC,
4157                         x + squareSize - overall.width - 2,
4158                         y + squareSize - font_descent - 1, string, 1);
4159         }
4160     }
4161     if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4162         string[0] = ONE + row;
4163         XTextExtents(coordFontStruct, string, 1, &direction,
4164                      &font_ascent, &font_descent, &overall);
4165         if (appData.monoMode) {
4166             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4167                              x + 2, y + font_ascent + 1, string, 1);
4168         } else {
4169             XDrawString(xDisplay, xBoardWindow, coordGC,
4170                         x + 2, y + font_ascent + 1, string, 1);
4171         }
4172     }
4173 }
4174
4175
4176 /* Why is this needed on some versions of X? */
4177 void EventProc(widget, unused, event)
4178      Widget widget;
4179      caddr_t unused;
4180      XEvent *event;
4181 {
4182     if (!XtIsRealized(widget))
4183       return;
4184
4185     switch (event->type) {
4186       case Expose:
4187         if (event->xexpose.count > 0) return;  /* no clipping is done */
4188         XDrawPosition(widget, True, NULL);
4189         break;
4190       default:
4191         return;
4192     }
4193 }
4194 /* end why */
4195
4196 void DrawPosition(fullRedraw, board)
4197      /*Boolean*/int fullRedraw;
4198      Board board;
4199 {
4200     XDrawPosition(boardWidget, fullRedraw, board);
4201 }
4202
4203 /* Returns 1 if there are "too many" differences between b1 and b2
4204    (i.e. more than 1 move was made) */
4205 static int too_many_diffs(b1, b2)
4206      Board b1, b2;
4207 {
4208     int i, j;
4209     int c = 0;
4210
4211     for (i=0; i<BOARD_HEIGHT; ++i) {
4212         for (j=0; j<BOARD_WIDTH; ++j) {
4213             if (b1[i][j] != b2[i][j]) {
4214                 if (++c > 4)    /* Castling causes 4 diffs */
4215                   return 1;
4216             }
4217         }
4218     }
4219
4220     return 0;
4221 }
4222
4223 /* Matrix describing castling maneuvers */
4224 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4225 static int castling_matrix[4][5] = {
4226     { 0, 0, 4, 3, 2 },          /* 0-0-0, white */
4227     { 0, 7, 4, 5, 6 },          /* 0-0,   white */
4228     { 7, 0, 4, 3, 2 },          /* 0-0-0, black */
4229     { 7, 7, 4, 5, 6 }           /* 0-0,   black */
4230 };
4231
4232 /* Checks whether castling occurred. If it did, *rrow and *rcol
4233    are set to the destination (row,col) of the rook that moved.
4234
4235    Returns 1 if castling occurred, 0 if not.
4236
4237    Note: Only handles a max of 1 castling move, so be sure
4238    to call too_many_diffs() first.
4239    */
4240 static int check_castle_draw(newb, oldb, rrow, rcol)
4241      Board newb, oldb;
4242      int *rrow, *rcol;
4243 {
4244     int i, *r, j;
4245     int match;
4246
4247     /* For each type of castling... */
4248     for (i=0; i<4; ++i) {
4249         r = castling_matrix[i];
4250
4251         /* Check the 4 squares involved in the castling move */
4252    &n