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