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