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