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