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