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