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