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