Implement -stickyWindows 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    <Btn2Motion>: HandlePV() \n \
1129    <Btn3Up>: PieceMenuPopup(menuB) \n \
1130    <Btn2Up>: PieceMenuPopup(menuB) \n \
1131    Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1132                  PieceMenuPopup(menuB) \n \
1133    Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1134                  PieceMenuPopup(menuW) \n \
1135    Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1136                  PieceMenuPopup(menuW) \n \
1137    Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1138                  PieceMenuPopup(menuB) \n";
1139
1140 char whiteTranslations[] =
1141    "Shift<BtnDown>: WhiteClock(1)\n \
1142    <BtnDown>: WhiteClock(0)\n";
1143 char blackTranslations[] =
1144    "Shift<BtnDown>: BlackClock(1)\n \
1145    <BtnDown>: BlackClock(0)\n";
1146
1147 char ICSInputTranslations[] =
1148     "<Key>Up: UpKeyProc() \n "
1149     "<Key>Down: DownKeyProc() \n "
1150     "<Key>Return: EnterKeyProc() \n";
1151
1152 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1153 //             as the widget is destroyed before the up-click can call extend-end
1154 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1155
1156 String xboardResources[] = {
1157     "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1158     "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1159     "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1160     NULL
1161   };
1162
1163
1164 /* Max possible square size */
1165 #define MAXSQSIZE 256
1166
1167 static int xpm_avail[MAXSQSIZE];
1168
1169 #ifdef HAVE_DIR_STRUCT
1170
1171 /* Extract piece size from filename */
1172 static int
1173 xpm_getsize (char *name, int len, char *ext)
1174 {
1175     char *p, *d;
1176     char buf[10];
1177
1178     if (len < 4)
1179       return 0;
1180
1181     if ((p=strchr(name, '.')) == NULL ||
1182         StrCaseCmp(p+1, ext) != 0)
1183       return 0;
1184
1185     p = name + 3;
1186     d = buf;
1187
1188     while (*p && isdigit(*p))
1189       *(d++) = *(p++);
1190
1191     *d = 0;
1192     return atoi(buf);
1193 }
1194
1195 /* Setup xpm_avail */
1196 static int
1197 xpm_getavail (char *dirname, char *ext)
1198 {
1199     DIR *dir;
1200     struct dirent *ent;
1201     int  i;
1202
1203     for (i=0; i<MAXSQSIZE; ++i)
1204       xpm_avail[i] = 0;
1205
1206     if (appData.debugMode)
1207       fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1208
1209     dir = opendir(dirname);
1210     if (!dir)
1211       {
1212           fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1213                   programName, dirname);
1214           exit(1);
1215       }
1216
1217     while ((ent=readdir(dir)) != NULL) {
1218         i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1219         if (i > 0 && i < MAXSQSIZE)
1220           xpm_avail[i] = 1;
1221     }
1222
1223     closedir(dir);
1224
1225     return 0;
1226 }
1227
1228 void
1229 xpm_print_avail (FILE *fp, char *ext)
1230 {
1231     int i;
1232
1233     fprintf(fp, _("Available `%s' sizes:\n"), ext);
1234     for (i=1; i<MAXSQSIZE; ++i) {
1235         if (xpm_avail[i])
1236           printf("%d\n", i);
1237     }
1238 }
1239
1240 /* Return XPM piecesize closest to size */
1241 int
1242 xpm_closest_to (char *dirname, int size, char *ext)
1243 {
1244     int i;
1245     int sm_diff = MAXSQSIZE;
1246     int sm_index = 0;
1247     int diff;
1248
1249     xpm_getavail(dirname, ext);
1250
1251     if (appData.debugMode)
1252       xpm_print_avail(stderr, ext);
1253
1254     for (i=1; i<MAXSQSIZE; ++i) {
1255         if (xpm_avail[i]) {
1256             diff = size - i;
1257             diff = (diff<0) ? -diff : diff;
1258             if (diff < sm_diff) {
1259                 sm_diff = diff;
1260                 sm_index = i;
1261             }
1262         }
1263     }
1264
1265     if (!sm_index) {
1266         fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1267         exit(1);
1268     }
1269
1270     return sm_index;
1271 }
1272 #else   /* !HAVE_DIR_STRUCT */
1273 /* If we are on a system without a DIR struct, we can't
1274    read the directory, so we can't collect a list of
1275    filenames, etc., so we can't do any size-fitting. */
1276 int
1277 xpm_closest_to (char *dirname, int size, char *ext)
1278 {
1279     fprintf(stderr, _("\
1280 Warning: No DIR structure found on this system --\n\
1281          Unable to autosize for XPM/XIM pieces.\n\
1282    Please report this error to %s.\n\
1283    Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1284     return size;
1285 }
1286 #endif /* HAVE_DIR_STRUCT */
1287
1288 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1289                              "magenta", "cyan", "white" };
1290 typedef struct {
1291     int attr, bg, fg;
1292 } TextColors;
1293 TextColors textColors[(int)NColorClasses];
1294
1295 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1296 static int
1297 parse_color (char *str, int which)
1298 {
1299     char *p, buf[100], *d;
1300     int i;
1301
1302     if (strlen(str) > 99)       /* watch bounds on buf */
1303       return -1;
1304
1305     p = str;
1306     d = buf;
1307     for (i=0; i<which; ++i) {
1308         p = strchr(p, ',');
1309         if (!p)
1310           return -1;
1311         ++p;
1312     }
1313
1314     /* Could be looking at something like:
1315        black, , 1
1316        .. in which case we want to stop on a comma also */
1317     while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1318       ++p;
1319
1320     if (*p == ',') {
1321         return -1;              /* Use default for empty field */
1322     }
1323
1324     if (which == 2 || isdigit(*p))
1325       return atoi(p);
1326
1327     while (*p && isalpha(*p))
1328       *(d++) = *(p++);
1329
1330     *d = 0;
1331
1332     for (i=0; i<8; ++i) {
1333         if (!StrCaseCmp(buf, cnames[i]))
1334           return which? (i+40) : (i+30);
1335     }
1336     if (!StrCaseCmp(buf, "default")) return -1;
1337
1338     fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1339     return -2;
1340 }
1341
1342 static int
1343 parse_cpair (ColorClass cc, char *str)
1344 {
1345     if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1346         fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1347                 programName, str);
1348         return -1;
1349     }
1350
1351     /* bg and attr are optional */
1352     textColors[(int)cc].bg = parse_color(str, 1);
1353     if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1354         textColors[(int)cc].attr = 0;
1355     }
1356     return 0;
1357 }
1358
1359
1360 /* Arrange to catch delete-window events */
1361 Atom wm_delete_window;
1362 void
1363 CatchDeleteWindow (Widget w, String procname)
1364 {
1365   char buf[MSG_SIZ];
1366   XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1367   snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1368   XtAugmentTranslations(w, XtParseTranslationTable(buf));
1369 }
1370
1371 void
1372 BoardToTop ()
1373 {
1374   Arg args[16];
1375   XtSetArg(args[0], XtNiconic, False);
1376   XtSetValues(shellWidget, args, 1);
1377
1378   XtPopup(shellWidget, XtGrabNone); /* Raise if lowered  */
1379 }
1380
1381 //---------------------------------------------------------------------------------------------------------
1382 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1383 #define XBOARD True
1384 #define JAWS_ARGS
1385 #define CW_USEDEFAULT (1<<31)
1386 #define ICS_TEXT_MENU_SIZE 90
1387 #define DEBUG_FILE "xboard.debug"
1388 #define SetCurrentDirectory chdir
1389 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1390 #define OPTCHAR "-"
1391 #define SEPCHAR " "
1392
1393 // these two must some day move to frontend.h, when they are implemented
1394 Boolean GameListIsUp();
1395
1396 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1397 #include "args.h"
1398
1399 // front-end part of option handling
1400
1401 // [HGM] This platform-dependent table provides the location for storing the color info
1402 extern char *crWhite, * crBlack;
1403
1404 void *
1405 colorVariable[] = {
1406   &appData.whitePieceColor,
1407   &appData.blackPieceColor,
1408   &appData.lightSquareColor,
1409   &appData.darkSquareColor,
1410   &appData.highlightSquareColor,
1411   &appData.premoveHighlightColor,
1412   &appData.lowTimeWarningColor,
1413   NULL,
1414   NULL,
1415   NULL,
1416   NULL,
1417   NULL,
1418   &crWhite,
1419   &crBlack,
1420   NULL
1421 };
1422
1423 // [HGM] font: keep a font for each square size, even non-stndard ones
1424 #define NUM_SIZES 18
1425 #define MAX_SIZE 130
1426 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1427 char *fontTable[NUM_FONTS][MAX_SIZE];
1428
1429 void
1430 ParseFont (char *name, int number)
1431 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1432   int size;
1433   if(sscanf(name, "size%d:", &size)) {
1434     // [HGM] font: font is meant for specific boardSize (likely from settings file);
1435     //       defer processing it until we know if it matches our board size
1436     if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1437         fontTable[number][size] = strdup(strchr(name, ':')+1);
1438         fontValid[number][size] = True;
1439     }
1440     return;
1441   }
1442   switch(number) {
1443     case 0: // CLOCK_FONT
1444         appData.clockFont = strdup(name);
1445       break;
1446     case 1: // MESSAGE_FONT
1447         appData.font = strdup(name);
1448       break;
1449     case 2: // COORD_FONT
1450         appData.coordFont = strdup(name);
1451       break;
1452     default:
1453       return;
1454   }
1455   fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1456 }
1457
1458 void
1459 SetFontDefaults ()
1460 { // only 2 fonts currently
1461   appData.clockFont = CLOCK_FONT_NAME;
1462   appData.coordFont = COORD_FONT_NAME;
1463   appData.font  =   DEFAULT_FONT_NAME;
1464 }
1465
1466 void
1467 CreateFonts ()
1468 { // no-op, until we identify the code for this already in XBoard and move it here
1469 }
1470
1471 void
1472 ParseColor (int n, char *name)
1473 { // in XBoard, just copy the color-name string
1474   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1475 }
1476
1477 void
1478 ParseTextAttribs (ColorClass cc, char *s)
1479 {
1480     (&appData.colorShout)[cc] = strdup(s);
1481 }
1482
1483 void
1484 ParseBoardSize (void *addr, char *name)
1485 {
1486     appData.boardSize = strdup(name);
1487 }
1488
1489 void
1490 LoadAllSounds ()
1491 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1492 }
1493
1494 void
1495 SetCommPortDefaults ()
1496 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1497 }
1498
1499 // [HGM] args: these three cases taken out to stay in front-end
1500 void
1501 SaveFontArg (FILE *f, ArgDescriptor *ad)
1502 {
1503   char *name;
1504   int i, n = (int)(intptr_t)ad->argLoc;
1505   switch(n) {
1506     case 0: // CLOCK_FONT
1507         name = appData.clockFont;
1508       break;
1509     case 1: // MESSAGE_FONT
1510         name = appData.font;
1511       break;
1512     case 2: // COORD_FONT
1513         name = appData.coordFont;
1514       break;
1515     default:
1516       return;
1517   }
1518   for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1519     if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1520         fontTable[n][squareSize] = strdup(name);
1521         fontValid[n][squareSize] = True;
1522         break;
1523   }
1524   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1525     fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1526 }
1527
1528 void
1529 ExportSounds ()
1530 { // nothing to do, as the sounds are at all times represented by their text-string names already
1531 }
1532
1533 void
1534 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1535 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1536         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1537 }
1538
1539 void
1540 SaveColor (FILE *f, ArgDescriptor *ad)
1541 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1542         if(colorVariable[(int)(intptr_t)ad->argLoc])
1543         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1544 }
1545
1546 void
1547 SaveBoardSize (FILE *f, char *name, void *addr)
1548 { // wrapper to shield back-end from BoardSize & sizeInfo
1549   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1550 }
1551
1552 void
1553 ParseCommPortSettings (char *s)
1554 { // no such option in XBoard (yet)
1555 }
1556
1557 extern Widget engineOutputShell;
1558 int frameX, frameY;
1559
1560 void
1561 GetActualPlacement (Widget wg, WindowPlacement *wp)
1562 {
1563   Arg args[16];
1564   Dimension w, h;
1565   Position x, y;
1566   XWindowAttributes winAt;
1567   Window win, dummy;
1568   int i, rx, ry;
1569
1570   if(!wg) return;
1571
1572     win = XtWindow(wg);
1573     XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
1574     XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
1575     wp->x = rx - winAt.x;
1576     wp->y = ry - winAt.y;
1577     wp->height = winAt.height;
1578     wp->width = winAt.width;
1579     frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1580 }
1581
1582 void
1583 GetWindowCoords ()
1584 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1585   // In XBoard this will have to wait until awareness of window parameters is implemented
1586   GetActualPlacement(shellWidget, &wpMain);
1587   if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1588   if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1589   if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1590   if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1591   if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1592   if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1593 }
1594
1595 void
1596 PrintCommPortSettings (FILE *f, char *name)
1597 { // This option does not exist in XBoard
1598 }
1599
1600 int
1601 MySearchPath (char *installDir, char *name, char *fullname)
1602 { // just append installDir and name. Perhaps ExpandPath should be used here?
1603   name = ExpandPathName(name);
1604   if(name && name[0] == '/')
1605     safeStrCpy(fullname, name, MSG_SIZ );
1606   else {
1607     sprintf(fullname, "%s%c%s", installDir, '/', name);
1608   }
1609   return 1;
1610 }
1611
1612 int
1613 MyGetFullPathName (char *name, char *fullname)
1614 { // should use ExpandPath?
1615   name = ExpandPathName(name);
1616   safeStrCpy(fullname, name, MSG_SIZ );
1617   return 1;
1618 }
1619
1620 void
1621 EnsureOnScreen (int *x, int *y, int minX, int minY)
1622 {
1623   return;
1624 }
1625
1626 int
1627 MainWindowUp ()
1628 { // [HGM] args: allows testing if main window is realized from back-end
1629   return xBoardWindow != 0;
1630 }
1631
1632 void
1633 PopUpStartupDialog ()
1634 {  // start menu not implemented in XBoard
1635 }
1636
1637 char *
1638 ConvertToLine (int argc, char **argv)
1639 {
1640   static char line[128*1024], buf[1024];
1641   int i;
1642
1643   line[0] = NULLCHAR;
1644   for(i=1; i<argc; i++)
1645     {
1646       if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1647           && argv[i][0] != '{' )
1648         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1649       else
1650         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1651       strncat(line, buf, 128*1024 - strlen(line) - 1 );
1652     }
1653
1654   line[strlen(line)-1] = NULLCHAR;
1655   return line;
1656 }
1657
1658 //--------------------------------------------------------------------------------------------
1659
1660 extern Boolean twoBoards, partnerUp;
1661
1662 #ifdef IDSIZES
1663   // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1664 #else
1665 #define BoardSize int
1666 void
1667 InitDrawingSizes (BoardSize boardSize, int flags)
1668 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1669     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1670     Arg args[16];
1671     XtGeometryResult gres;
1672     int i;
1673     static Dimension oldWidth, oldHeight;
1674     static VariantClass oldVariant;
1675     static int oldDual = -1, oldMono = -1;
1676
1677     if(!formWidget) return;
1678
1679     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1680     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1681     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1682
1683   if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1684     /*
1685      * Enable shell resizing.
1686      */
1687     shellArgs[0].value = (XtArgVal) &w;
1688     shellArgs[1].value = (XtArgVal) &h;
1689     XtGetValues(shellWidget, shellArgs, 2);
1690
1691     shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1692     shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1693     XtSetValues(shellWidget, &shellArgs[2], 4);
1694
1695     XtSetArg(args[0], XtNdefaultDistance, &sep);
1696     XtGetValues(formWidget, args, 1);
1697
1698     oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1699     CreateGrid();
1700     hOffset = boardWidth + 10;
1701     for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1702         secondSegments[i] = gridSegments[i];
1703         secondSegments[i].x1 += hOffset;
1704         secondSegments[i].x2 += hOffset;
1705     }
1706
1707     XtSetArg(args[0], XtNwidth, boardWidth);
1708     XtSetArg(args[1], XtNheight, boardHeight);
1709     XtSetValues(boardWidget, args, 2);
1710
1711     timerWidth = (boardWidth - sep) / 2;
1712     XtSetArg(args[0], XtNwidth, timerWidth);
1713     XtSetValues(whiteTimerWidget, args, 1);
1714     XtSetValues(blackTimerWidget, args, 1);
1715
1716     XawFormDoLayout(formWidget, False);
1717
1718     if (appData.titleInWindow) {
1719         i = 0;
1720         XtSetArg(args[i], XtNborderWidth, &bor); i++;
1721         XtSetArg(args[i], XtNheight, &h);  i++;
1722         XtGetValues(titleWidget, args, i);
1723         if (smallLayout) {
1724             w = boardWidth - 2*bor;
1725         } else {
1726             XtSetArg(args[0], XtNwidth, &w);
1727             XtGetValues(menuBarWidget, args, 1);
1728             w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1729         }
1730
1731         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1732         if (gres != XtGeometryYes && appData.debugMode) {
1733             fprintf(stderr,
1734                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1735                     programName, gres, w, h, wr, hr);
1736         }
1737     }
1738
1739     XawFormDoLayout(formWidget, True);
1740
1741     /*
1742      * Inhibit shell resizing.
1743      */
1744     shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1745     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1746     shellArgs[4].value = shellArgs[2].value = w;
1747     shellArgs[5].value = shellArgs[3].value = h;
1748     XtSetValues(shellWidget, &shellArgs[0], 6);
1749   }
1750
1751     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1752     // (only for xpm)
1753
1754   if(gameInfo.variant != oldVariant) { // and only if variant changed
1755
1756     if(useImages) {
1757       for(i=0; i<4; i++) {
1758         int p;
1759         for(p=0; p<=(int)WhiteKing; p++)
1760            xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1761         if(gameInfo.variant == VariantShogi) {
1762            xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1763            xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1764            xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1765            xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1766            xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1767         }
1768 #ifdef GOTHIC
1769         if(gameInfo.variant == VariantGothic) {
1770            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1771         }
1772 #endif
1773         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1774            xpmPieceBitmap[i][(int)WhiteAngel]    = xpmPieceBitmap2[i][(int)WhiteFalcon];
1775            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1776         }
1777 #if !HAVE_LIBXPM
1778         // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1779         for(p=0; p<=(int)WhiteKing; p++)
1780            ximMaskPm[p] = ximMaskPm2[p]; // defaults
1781         if(gameInfo.variant == VariantShogi) {
1782            ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1783            ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1784            ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1785            ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1786            ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1787         }
1788 #ifdef GOTHIC
1789         if(gameInfo.variant == VariantGothic) {
1790            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1791         }
1792 #endif
1793         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1794            ximMaskPm[(int)WhiteAngel]    = ximMaskPm2[(int)WhiteFalcon];
1795            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1796         }
1797 #endif
1798       }
1799     } else {
1800       for(i=0; i<2; i++) {
1801         int p;
1802         for(p=0; p<=(int)WhiteKing; p++)
1803            pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1804         if(gameInfo.variant == VariantShogi) {
1805            pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1806            pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1807            pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1808            pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1809            pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1810         }
1811 #ifdef GOTHIC
1812         if(gameInfo.variant == VariantGothic) {
1813            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1814         }
1815 #endif
1816         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1817            pieceBitmap[i][(int)WhiteAngel]    = pieceBitmap2[i][(int)WhiteFalcon];
1818            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1819         }
1820       }
1821     }
1822     oldMono = -10; // kludge to force recreation of animation masks
1823   }
1824 #if HAVE_LIBXPM
1825   if(appData.monoMode != oldMono)
1826     CreateAnimVars();
1827 #endif
1828   oldMono = appData.monoMode;
1829 }
1830 #endif
1831
1832 void
1833 ParseIcsTextColors ()
1834 {   // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1835     if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1836         parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1837         parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
1838         parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
1839         parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1840         parse_cpair(ColorTell, appData.colorTell) < 0 ||
1841         parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
1842         parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
1843         parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
1844         parse_cpair(ColorNormal, appData.colorNormal) < 0)
1845       {
1846           if (appData.colorize) {
1847               fprintf(stderr,
1848                       _("%s: can't parse color names; disabling colorization\n"),
1849                       programName);
1850           }
1851           appData.colorize = FALSE;
1852       }
1853 }
1854
1855 int
1856 MakeColors ()
1857 {   // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1858     XrmValue vFrom, vTo;
1859     int forceMono = False;
1860
1861     if (!appData.monoMode) {
1862         vFrom.addr = (caddr_t) appData.lightSquareColor;
1863         vFrom.size = strlen(appData.lightSquareColor);
1864         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1865         if (vTo.addr == NULL) {
1866           appData.monoMode = True;
1867           forceMono = True;
1868         } else {
1869           lightSquareColor = *(Pixel *) vTo.addr;
1870         }
1871     }
1872     if (!appData.monoMode) {
1873         vFrom.addr = (caddr_t) appData.darkSquareColor;
1874         vFrom.size = strlen(appData.darkSquareColor);
1875         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1876         if (vTo.addr == NULL) {
1877           appData.monoMode = True;
1878           forceMono = True;
1879         } else {
1880           darkSquareColor = *(Pixel *) vTo.addr;
1881         }
1882     }
1883     if (!appData.monoMode) {
1884         vFrom.addr = (caddr_t) appData.whitePieceColor;
1885         vFrom.size = strlen(appData.whitePieceColor);
1886         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1887         if (vTo.addr == NULL) {
1888           appData.monoMode = True;
1889           forceMono = True;
1890         } else {
1891           whitePieceColor = *(Pixel *) vTo.addr;
1892         }
1893     }
1894     if (!appData.monoMode) {
1895         vFrom.addr = (caddr_t) appData.blackPieceColor;
1896         vFrom.size = strlen(appData.blackPieceColor);
1897         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1898         if (vTo.addr == NULL) {
1899           appData.monoMode = True;
1900           forceMono = True;
1901         } else {
1902           blackPieceColor = *(Pixel *) vTo.addr;
1903         }
1904     }
1905
1906     if (!appData.monoMode) {
1907         vFrom.addr = (caddr_t) appData.highlightSquareColor;
1908         vFrom.size = strlen(appData.highlightSquareColor);
1909         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1910         if (vTo.addr == NULL) {
1911           appData.monoMode = True;
1912           forceMono = True;
1913         } else {
1914           highlightSquareColor = *(Pixel *) vTo.addr;
1915         }
1916     }
1917
1918     if (!appData.monoMode) {
1919         vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1920         vFrom.size = strlen(appData.premoveHighlightColor);
1921         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1922         if (vTo.addr == NULL) {
1923           appData.monoMode = True;
1924           forceMono = True;
1925         } else {
1926           premoveHighlightColor = *(Pixel *) vTo.addr;
1927         }
1928     }
1929     return forceMono;
1930 }
1931
1932 void
1933 CreateAnyPieces ()
1934 {   // [HGM] taken out of main
1935 #if HAVE_LIBXPM
1936     if (appData.monoMode && // [HGM] no sense to go on to certain doom
1937        (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1938             appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1939
1940     if (appData.bitmapDirectory[0] != NULLCHAR) {
1941       CreatePieces();
1942     } else {
1943       CreateXPMPieces();
1944       CreateXPMBoard(appData.liteBackTextureFile, 1);
1945       CreateXPMBoard(appData.darkBackTextureFile, 0);
1946     }
1947 #else
1948     CreateXIMPieces();
1949     /* Create regular pieces */
1950     if (!useImages) CreatePieces();
1951 #endif
1952 }
1953
1954 int
1955 main (int argc, char **argv)
1956 {
1957     int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1958     XSetWindowAttributes window_attributes;
1959     Arg args[16];
1960     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1961     XrmValue vFrom, vTo;
1962     XtGeometryResult gres;
1963     char *p;
1964     XrmDatabase xdb;
1965     int forceMono = False;
1966
1967     srandom(time(0)); // [HGM] book: make random truly random
1968
1969     setbuf(stdout, NULL);
1970     setbuf(stderr, NULL);
1971     debugFP = stderr;
1972
1973     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1974         printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1975         exit(0);
1976     }
1977
1978     programName = strrchr(argv[0], '/');
1979     if (programName == NULL)
1980       programName = argv[0];
1981     else
1982       programName++;
1983
1984 #ifdef ENABLE_NLS
1985     XtSetLanguageProc(NULL, NULL, NULL);
1986     bindtextdomain(PACKAGE, LOCALEDIR);
1987     textdomain(PACKAGE);
1988 #endif
1989
1990     shellWidget =
1991       XtAppInitialize(&appContext, "XBoard", shellOptions,
1992                       XtNumber(shellOptions),
1993                       &argc, argv, xboardResources, NULL, 0);
1994     appData.boardSize = "";
1995     InitAppData(ConvertToLine(argc, argv));
1996     p = getenv("HOME");
1997     if (p == NULL) p = "/tmp";
1998     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1999     gameCopyFilename = (char*) malloc(i);
2000     gamePasteFilename = (char*) malloc(i);
2001     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
2002     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2003
2004     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2005                               clientResources, XtNumber(clientResources),
2006                               NULL, 0);
2007
2008     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2009         static char buf[MSG_SIZ];
2010         EscapeExpand(buf, appData.firstInitString);
2011         appData.firstInitString = strdup(buf);
2012         EscapeExpand(buf, appData.secondInitString);
2013         appData.secondInitString = strdup(buf);
2014         EscapeExpand(buf, appData.firstComputerString);
2015         appData.firstComputerString = strdup(buf);
2016         EscapeExpand(buf, appData.secondComputerString);
2017         appData.secondComputerString = strdup(buf);
2018     }
2019
2020     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2021         chessDir = ".";
2022     } else {
2023         if (chdir(chessDir) != 0) {
2024             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2025             perror(chessDir);
2026             exit(1);
2027         }
2028     }
2029
2030     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2031         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2032         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
2033            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2034            exit(errno);
2035         }
2036         setbuf(debugFP, NULL);
2037     }
2038
2039 #if ENABLE_NLS
2040     if (appData.debugMode) {
2041       fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2042     }
2043 #endif
2044
2045     /* [HGM,HR] make sure board size is acceptable */
2046     if(appData.NrFiles > BOARD_FILES ||
2047        appData.NrRanks > BOARD_RANKS   )
2048          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2049
2050 #if !HIGHDRAG
2051     /* This feature does not work; animation needs a rewrite */
2052     appData.highlightDragging = FALSE;
2053 #endif
2054     InitBackEnd1();
2055
2056     xDisplay = XtDisplay(shellWidget);
2057     xScreen = DefaultScreen(xDisplay);
2058     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2059
2060         gameInfo.variant = StringToVariant(appData.variant);
2061         InitPosition(FALSE);
2062
2063 #ifdef IDSIZE
2064     InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2065 #else
2066     if (isdigit(appData.boardSize[0])) {
2067         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2068                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2069                    &fontPxlSize, &smallLayout, &tinyLayout);
2070         if (i == 0) {
2071             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2072                     programName, appData.boardSize);
2073             exit(2);
2074         }
2075         if (i < 7) {
2076             /* Find some defaults; use the nearest known size */
2077             SizeDefaults *szd, *nearest;
2078             int distance = 99999;
2079             nearest = szd = sizeDefaults;
2080             while (szd->name != NULL) {
2081                 if (abs(szd->squareSize - squareSize) < distance) {
2082                     nearest = szd;
2083                     distance = abs(szd->squareSize - squareSize);
2084                     if (distance == 0) break;
2085                 }
2086                 szd++;
2087             }
2088             if (i < 2) lineGap = nearest->lineGap;
2089             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2090             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2091             if (i < 5) fontPxlSize = nearest->fontPxlSize;
2092             if (i < 6) smallLayout = nearest->smallLayout;
2093             if (i < 7) tinyLayout = nearest->tinyLayout;
2094         }
2095     } else {
2096         SizeDefaults *szd = sizeDefaults;
2097         if (*appData.boardSize == NULLCHAR) {
2098             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2099                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2100               szd++;
2101             }
2102             if (szd->name == NULL) szd--;
2103             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2104         } else {
2105             while (szd->name != NULL &&
2106                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2107             if (szd->name == NULL) {
2108                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2109                         programName, appData.boardSize);
2110                 exit(2);
2111             }
2112         }
2113         squareSize = szd->squareSize;
2114         lineGap = szd->lineGap;
2115         clockFontPxlSize = szd->clockFontPxlSize;
2116         coordFontPxlSize = szd->coordFontPxlSize;
2117         fontPxlSize = szd->fontPxlSize;
2118         smallLayout = szd->smallLayout;
2119         tinyLayout = szd->tinyLayout;
2120         // [HGM] font: use defaults from settings file if available and not overruled
2121     }
2122     if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2123         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2124     if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2125         appData.font = fontTable[MESSAGE_FONT][squareSize];
2126     if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2127         appData.coordFont = fontTable[COORD_FONT][squareSize];
2128
2129     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2130     if (strlen(appData.pixmapDirectory) > 0) {
2131         p = ExpandPathName(appData.pixmapDirectory);
2132         if (!p) {
2133             fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2134                    appData.pixmapDirectory);
2135             exit(1);
2136         }
2137         if (appData.debugMode) {
2138           fprintf(stderr, _("\
2139 XBoard square size (hint): %d\n\
2140 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2141         }
2142         squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2143         if (appData.debugMode) {
2144             fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2145         }
2146     }
2147     defaultLineGap = lineGap;
2148     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2149
2150     /* [HR] height treated separately (hacked) */
2151     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2152     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2153     if (appData.showJail == 1) {
2154         /* Jail on top and bottom */
2155         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2156         XtSetArg(boardArgs[2], XtNheight,
2157                  boardHeight + 2*(lineGap + squareSize));
2158     } else if (appData.showJail == 2) {
2159         /* Jail on sides */
2160         XtSetArg(boardArgs[1], XtNwidth,
2161                  boardWidth + 2*(lineGap + squareSize));
2162         XtSetArg(boardArgs[2], XtNheight, boardHeight);
2163     } else {
2164         /* No jail */
2165         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2166         XtSetArg(boardArgs[2], XtNheight, boardHeight);
2167     }
2168
2169     /*
2170      * Determine what fonts to use.
2171      */
2172 #if ENABLE_NLS
2173     appData.font = InsertPxlSize(appData.font, fontPxlSize);
2174     appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2175     appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2176     fontSet = CreateFontSet(appData.font);
2177     clockFontSet = CreateFontSet(appData.clockFont);
2178     {
2179       /* For the coordFont, use the 0th font of the fontset. */
2180       XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2181       XFontStruct **font_struct_list;
2182       XFontSetExtents *fontSize;
2183       char **font_name_list;
2184       XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2185       coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2186       coordFontStruct = XQueryFont(xDisplay, coordFontID);
2187       fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
2188       textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
2189     }
2190 #else
2191     appData.font = FindFont(appData.font, fontPxlSize);
2192     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2193     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2194     clockFontID = XLoadFont(xDisplay, appData.clockFont);
2195     clockFontStruct = XQueryFont(xDisplay, clockFontID);
2196     coordFontID = XLoadFont(xDisplay, appData.coordFont);
2197     coordFontStruct = XQueryFont(xDisplay, coordFontID);
2198 #endif
2199     countFontID = coordFontID;  // [HGM] holdings
2200     countFontStruct = coordFontStruct;
2201
2202     xdb = XtDatabase(xDisplay);
2203 #if ENABLE_NLS
2204     XrmPutLineResource(&xdb, "*international: True");
2205     vTo.size = sizeof(XFontSet);
2206     vTo.addr = (XtPointer) &fontSet;
2207     XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2208 #else
2209     XrmPutStringResource(&xdb, "*font", appData.font);
2210 #endif
2211
2212     /*
2213      * Detect if there are not enough colors available and adapt.
2214      */
2215     if (DefaultDepth(xDisplay, xScreen) <= 2) {
2216       appData.monoMode = True;
2217     }
2218
2219     forceMono = MakeColors();
2220
2221     if (forceMono) {
2222       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2223               programName);
2224         appData.monoMode = True;
2225     }
2226
2227     if (appData.lowTimeWarning && !appData.monoMode) {
2228       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2229       vFrom.size = strlen(appData.lowTimeWarningColor);
2230       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2231       if (vTo.addr == NULL)
2232                 appData.monoMode = True;
2233       else
2234                 lowTimeWarningColor = *(Pixel *) vTo.addr;
2235     }
2236
2237     if (appData.monoMode && appData.debugMode) {
2238         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2239                 (unsigned long) XWhitePixel(xDisplay, xScreen),
2240                 (unsigned long) XBlackPixel(xDisplay, xScreen));
2241     }
2242
2243     ParseIcsTextColors();
2244     textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2245     textColors[ColorNone].attr = 0;
2246
2247     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2248
2249     /*
2250      * widget hierarchy
2251      */
2252     if (tinyLayout) {
2253         layoutName = "tinyLayout";
2254     } else if (smallLayout) {
2255         layoutName = "smallLayout";
2256     } else {
2257         layoutName = "normalLayout";
2258     }
2259     /* Outer layoutWidget is there only to provide a name for use in
2260        resources that depend on the layout style */
2261     layoutWidget =
2262       XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2263                             layoutArgs, XtNumber(layoutArgs));
2264     formWidget =
2265       XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2266                             formArgs, XtNumber(formArgs));
2267     XtSetArg(args[0], XtNdefaultDistance, &sep);
2268     XtGetValues(formWidget, args, 1);
2269
2270     j = 0;
2271     widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
2272     XtSetArg(args[0], XtNtop,    XtChainTop);
2273     XtSetArg(args[1], XtNbottom, XtChainTop);
2274     XtSetArg(args[2], XtNright,  XtChainLeft);
2275     XtSetValues(menuBarWidget, args, 3);
2276
2277     widgetList[j++] = whiteTimerWidget =
2278       XtCreateWidget("whiteTime", labelWidgetClass,
2279                      formWidget, timerArgs, XtNumber(timerArgs));
2280 #if ENABLE_NLS
2281     XtSetArg(args[0], XtNfontSet, clockFontSet);
2282 #else
2283     XtSetArg(args[0], XtNfont, clockFontStruct);
2284 #endif
2285     XtSetArg(args[1], XtNtop,    XtChainTop);
2286     XtSetArg(args[2], XtNbottom, XtChainTop);
2287     XtSetValues(whiteTimerWidget, args, 3);
2288
2289     widgetList[j++] = blackTimerWidget =
2290       XtCreateWidget("blackTime", labelWidgetClass,
2291                      formWidget, timerArgs, XtNumber(timerArgs));
2292 #if ENABLE_NLS
2293     XtSetArg(args[0], XtNfontSet, clockFontSet);
2294 #else
2295     XtSetArg(args[0], XtNfont, clockFontStruct);
2296 #endif
2297     XtSetArg(args[1], XtNtop,    XtChainTop);
2298     XtSetArg(args[2], XtNbottom, XtChainTop);
2299     XtSetValues(blackTimerWidget, args, 3);
2300
2301     if (appData.titleInWindow) {
2302         widgetList[j++] = titleWidget =
2303           XtCreateWidget("title", labelWidgetClass, formWidget,
2304                          titleArgs, XtNumber(titleArgs));
2305         XtSetArg(args[0], XtNtop,    XtChainTop);
2306         XtSetArg(args[1], XtNbottom, XtChainTop);
2307         XtSetValues(titleWidget, args, 2);
2308     }
2309
2310     if (appData.showButtonBar) {
2311       widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2312       XtSetArg(args[0], XtNleft,  XtChainRight); // [HGM] glue to right window edge
2313       XtSetArg(args[1], XtNright, XtChainRight); //       for good run-time sizing
2314       XtSetArg(args[2], XtNtop,    XtChainTop);
2315       XtSetArg(args[3], XtNbottom, XtChainTop);
2316       XtSetValues(buttonBarWidget, args, 4);
2317     }
2318
2319     widgetList[j++] = messageWidget =
2320       XtCreateWidget("message", labelWidgetClass, formWidget,
2321                      messageArgs, XtNumber(messageArgs));
2322     XtSetArg(args[0], XtNtop,    XtChainTop);
2323     XtSetArg(args[1], XtNbottom, XtChainTop);
2324     XtSetValues(messageWidget, args, 2);
2325
2326     widgetList[j++] = boardWidget =
2327       XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2328                      XtNumber(boardArgs));
2329
2330     XtManageChildren(widgetList, j);
2331
2332     timerWidth = (boardWidth - sep) / 2;
2333     XtSetArg(args[0], XtNwidth, timerWidth);
2334     XtSetValues(whiteTimerWidget, args, 1);
2335     XtSetValues(blackTimerWidget, args, 1);
2336
2337     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2338     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2339     XtGetValues(whiteTimerWidget, args, 2);
2340
2341     if (appData.showButtonBar) {
2342       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2343       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2344       XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2345     }
2346
2347     /*
2348      * formWidget uses these constraints but they are stored
2349      * in the children.
2350      */
2351     i = 0;
2352     XtSetArg(args[i], XtNfromHoriz, 0); i++;
2353     XtSetValues(menuBarWidget, args, i);
2354     if (appData.titleInWindow) {
2355         if (smallLayout) {
2356             i = 0;
2357             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2358             XtSetValues(whiteTimerWidget, args, i);
2359             i = 0;
2360             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2361             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2362             XtSetValues(blackTimerWidget, args, i);
2363             i = 0;
2364             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2365             XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2366             XtSetValues(titleWidget, args, i);
2367             i = 0;
2368             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2369             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2370             XtSetValues(messageWidget, args, i);
2371             if (appData.showButtonBar) {
2372               i = 0;
2373               XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2374               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2375               XtSetValues(buttonBarWidget, args, i);
2376             }
2377         } else {
2378             i = 0;
2379             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2380             XtSetValues(whiteTimerWidget, args, i);
2381             i = 0;
2382             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2383             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2384             XtSetValues(blackTimerWidget, args, i);
2385             i = 0;
2386             XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2387             XtSetValues(titleWidget, args, i);
2388             i = 0;
2389             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2390             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2391             XtSetValues(messageWidget, args, i);
2392             if (appData.showButtonBar) {
2393               i = 0;
2394               XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2395               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2396               XtSetValues(buttonBarWidget, args, i);
2397             }
2398         }
2399     } else {
2400         i = 0;
2401         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2402         XtSetValues(whiteTimerWidget, args, i);
2403         i = 0;
2404         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2405         XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2406         XtSetValues(blackTimerWidget, args, i);
2407         i = 0;
2408         XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2409         XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2410         XtSetValues(messageWidget, args, i);
2411         if (appData.showButtonBar) {
2412           i = 0;
2413           XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2414           XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2415           XtSetValues(buttonBarWidget, args, i);
2416         }
2417     }
2418     i = 0;
2419     XtSetArg(args[0], XtNfromVert, messageWidget);
2420     XtSetArg(args[1], XtNtop,    XtChainTop);
2421     XtSetArg(args[2], XtNbottom, XtChainBottom);
2422     XtSetArg(args[3], XtNleft,   XtChainLeft);
2423     XtSetArg(args[4], XtNright,  XtChainRight);
2424     XtSetValues(boardWidget, args, 5);
2425
2426     XtRealizeWidget(shellWidget);
2427
2428     if(wpMain.x > 0) {
2429       XtSetArg(args[0], XtNx, wpMain.x);
2430       XtSetArg(args[1], XtNy, wpMain.y);
2431       XtSetValues(shellWidget, args, 2);
2432     }
2433
2434     /*
2435      * Correct the width of the message and title widgets.
2436      * It is not known why some systems need the extra fudge term.
2437      * The value "2" is probably larger than needed.
2438      */
2439     XawFormDoLayout(formWidget, False);
2440
2441 #define WIDTH_FUDGE 2
2442     i = 0;
2443     XtSetArg(args[i], XtNborderWidth, &bor);  i++;
2444     XtSetArg(args[i], XtNheight, &h);  i++;
2445     XtGetValues(messageWidget, args, i);
2446     if (appData.showButtonBar) {
2447       i = 0;
2448       XtSetArg(args[i], XtNwidth, &w);  i++;
2449       XtGetValues(buttonBarWidget, args, i);
2450       w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2451     } else {
2452       w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2453     }
2454
2455     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2456     if (gres != XtGeometryYes && appData.debugMode) {
2457       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2458               programName, gres, w, h, wr, hr);
2459     }
2460
2461     /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2462     /* The size used for the child widget in layout lags one resize behind
2463        its true size, so we resize a second time, 1 pixel smaller.  Yeech! */
2464     w--;
2465     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2466     if (gres != XtGeometryYes && appData.debugMode) {
2467       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2468               programName, gres, w, h, wr, hr);
2469     }
2470     /* !! end hack */
2471     if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
2472     XtSetArg(args[0], XtNleft,  XtChainLeft);  // [HGM] glue ends for good run-time sizing
2473     XtSetArg(args[1], XtNright, XtChainRight);
2474     XtSetValues(messageWidget, args, 2);
2475
2476     if (appData.titleInWindow) {
2477         i = 0;
2478         XtSetArg(args[i], XtNborderWidth, &bor); i++;
2479         XtSetArg(args[i], XtNheight, &h);  i++;
2480         XtGetValues(titleWidget, args, i);
2481         if (smallLayout) {
2482             w = boardWidth - 2*bor;
2483         } else {
2484             XtSetArg(args[0], XtNwidth, &w);
2485             XtGetValues(menuBarWidget, args, 1);
2486             w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2487         }
2488
2489         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2490         if (gres != XtGeometryYes && appData.debugMode) {
2491             fprintf(stderr,
2492                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2493                     programName, gres, w, h, wr, hr);
2494         }
2495     }
2496     XawFormDoLayout(formWidget, True);
2497
2498     xBoardWindow = XtWindow(boardWidget);
2499
2500     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2501     //       not need to go into InitDrawingSizes().
2502 #endif
2503
2504     /*
2505      * Create X checkmark bitmap and initialize option menu checks.
2506      */
2507     ReadBitmap(&xMarkPixmap, "checkmark.bm",
2508                checkmark_bits, checkmark_width, checkmark_height);
2509     XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2510 #ifndef OPTIONSDIALOG
2511     if (appData.alwaysPromoteToQueen) {
2512         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2513                     args, 1);
2514     }
2515     if (appData.animateDragging) {
2516         XtSetValues(XtNameToWidget(menuBarWidget,
2517                                    "menuOptions.Animate Dragging"),
2518                     args, 1);
2519     }
2520     if (appData.animate) {
2521         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2522                     args, 1);
2523     }
2524     if (appData.autoCallFlag) {
2525         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2526                     args, 1);
2527     }
2528     if (appData.autoFlipView) {
2529         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2530                     args, 1);
2531     }
2532     if (appData.blindfold) {
2533         XtSetValues(XtNameToWidget(menuBarWidget,
2534                                    "menuOptions.Blindfold"), args, 1);
2535     }
2536     if (appData.flashCount > 0) {
2537         XtSetValues(XtNameToWidget(menuBarWidget,
2538                                    "menuOptions.Flash Moves"),
2539                     args, 1);
2540     }
2541 #if HIGHDRAG
2542     if (appData.highlightDragging) {
2543         XtSetValues(XtNameToWidget(menuBarWidget,
2544                                    "menuOptions.Highlight Dragging"),
2545                     args, 1);
2546     }
2547 #endif
2548     if (appData.highlightLastMove) {
2549         XtSetValues(XtNameToWidget(menuBarWidget,
2550                                    "menuOptions.Highlight Last Move"),
2551                     args, 1);
2552     }
2553     if (appData.highlightMoveWithArrow) {
2554         XtSetValues(XtNameToWidget(menuBarWidget,
2555                                    "menuOptions.Arrow"),
2556                     args, 1);
2557     }
2558 //    if (appData.icsAlarm) {
2559 //      XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2560 //                  args, 1);
2561 //    }
2562     if (appData.ringBellAfterMoves) {
2563         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2564                     args, 1);
2565     }
2566     if (appData.oneClick) {
2567         XtSetValues(XtNameToWidget(menuBarWidget,
2568                                    "menuOptions.OneClick"), args, 1);
2569     }
2570     if (appData.periodicUpdates) {
2571         XtSetValues(XtNameToWidget(menuBarWidget,
2572                                    "menuOptions.Periodic Updates"), args, 1);
2573     }
2574     if (appData.ponderNextMove) {
2575         XtSetValues(XtNameToWidget(menuBarWidget,
2576                                    "menuOptions.Ponder Next Move"), args, 1);
2577     }
2578     if (appData.popupExitMessage) {
2579         XtSetValues(XtNameToWidget(menuBarWidget,
2580                                    "menuOptions.Popup Exit Message"), args, 1);
2581     }
2582     if (appData.popupMoveErrors) {
2583         XtSetValues(XtNameToWidget(menuBarWidget,
2584                                    "menuOptions.Popup Move Errors"), args, 1);
2585     }
2586 //    if (appData.premove) {
2587 //      XtSetValues(XtNameToWidget(menuBarWidget,
2588 //                                 "menuOptions.Premove"), args, 1);
2589 //    }
2590     if (appData.showCoords) {
2591         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2592                     args, 1);
2593     }
2594     if (appData.hideThinkingFromHuman) {
2595         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2596                     args, 1);
2597     }
2598     if (appData.testLegality) {
2599         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2600                     args, 1);
2601     }
2602 #endif
2603     if (saveSettingsOnExit) {
2604         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2605                     args, 1);
2606     }
2607
2608     /*
2609      * Create an icon.
2610      */
2611     ReadBitmap(&wIconPixmap, "icon_white.bm",
2612                icon_white_bits, icon_white_width, icon_white_height);
2613     ReadBitmap(&bIconPixmap, "icon_black.bm",
2614                icon_black_bits, icon_black_width, icon_black_height);
2615     iconPixmap = wIconPixmap;
2616     i = 0;
2617     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
2618     XtSetValues(shellWidget, args, i);
2619
2620     /*
2621      * Create a cursor for the board widget.
2622      */
2623     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2624     XChangeWindowAttributes(xDisplay, xBoardWindow,
2625                             CWCursor, &window_attributes);
2626
2627     /*
2628      * Inhibit shell resizing.
2629      */
2630     shellArgs[0].value = (XtArgVal) &w;
2631     shellArgs[1].value = (XtArgVal) &h;
2632     XtGetValues(shellWidget, shellArgs, 2);
2633     shellArgs[4].value = shellArgs[2].value = w;
2634     shellArgs[5].value = shellArgs[3].value = h;
2635     XtSetValues(shellWidget, &shellArgs[2], 4);
2636     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2637     marginH =  h - boardHeight;
2638
2639     CatchDeleteWindow(shellWidget, "QuitProc");
2640
2641     CreateGCs(False);
2642     CreateGrid();
2643     CreateAnyPieces();
2644
2645     CreatePieceMenus();
2646
2647     if (appData.animate || appData.animateDragging)
2648       CreateAnimVars();
2649
2650     XtAugmentTranslations(formWidget,
2651                           XtParseTranslationTable(globalTranslations));
2652     XtAugmentTranslations(boardWidget,
2653                           XtParseTranslationTable(boardTranslations));
2654     XtAugmentTranslations(whiteTimerWidget,
2655                           XtParseTranslationTable(whiteTranslations));
2656     XtAugmentTranslations(blackTimerWidget,
2657                           XtParseTranslationTable(blackTranslations));
2658
2659     /* Why is the following needed on some versions of X instead
2660      * of a translation? */
2661     XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2662                       (XtEventHandler) EventProc, NULL);
2663     /* end why */
2664     XtAddEventHandler(formWidget, KeyPressMask, False,
2665                       (XtEventHandler) MoveTypeInProc, NULL);
2666     XtAddEventHandler(shellWidget, StructureNotifyMask, False,
2667                       (XtEventHandler) EventProc, NULL);
2668
2669     /* [AS] Restore layout */
2670     if( wpMoveHistory.visible ) {
2671       HistoryPopUp();
2672     }
2673
2674     if( wpEvalGraph.visible )
2675       {
2676         EvalGraphPopUp();
2677       };
2678
2679     if( wpEngineOutput.visible ) {
2680       EngineOutputPopUp();
2681     }
2682
2683     InitBackEnd2();
2684
2685     if (errorExitStatus == -1) {
2686         if (appData.icsActive) {
2687             /* We now wait until we see "login:" from the ICS before
2688                sending the logon script (problems with timestamp otherwise) */
2689             /*ICSInitScript();*/
2690             if (appData.icsInputBox) ICSInputBoxPopUp();
2691         }
2692
2693     #ifdef SIGWINCH
2694     signal(SIGWINCH, TermSizeSigHandler);
2695     #endif
2696         signal(SIGINT, IntSigHandler);
2697         signal(SIGTERM, IntSigHandler);
2698         if (*appData.cmailGameName != NULLCHAR) {
2699             signal(SIGUSR1, CmailSigHandler);
2700         }
2701     }
2702     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2703     InitPosition(TRUE);
2704 //    XtSetKeyboardFocus(shellWidget, formWidget);
2705     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2706
2707     XtAppMainLoop(appContext);
2708     if (appData.debugMode) fclose(debugFP); // [DM] debug
2709     return 0;
2710 }
2711
2712 static Boolean noEcho;
2713
2714 void
2715 ShutDownFrontEnd ()
2716 {
2717     if (appData.icsActive && oldICSInteractionTitle != NULL) {
2718         DisplayIcsInteractionTitle(oldICSInteractionTitle);
2719     }
2720     if (saveSettingsOnExit) SaveSettings(settingsFileName);
2721     unlink(gameCopyFilename);
2722     unlink(gamePasteFilename);
2723     if(noEcho) EchoOn();
2724 }
2725
2726 RETSIGTYPE
2727 TermSizeSigHandler (int sig)
2728 {
2729     update_ics_width();
2730 }
2731
2732 RETSIGTYPE
2733 IntSigHandler (int sig)
2734 {
2735     ExitEvent(sig);
2736 }
2737
2738 RETSIGTYPE
2739 CmailSigHandler (int sig)
2740 {
2741     int dummy = 0;
2742     int error;
2743
2744     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
2745
2746     /* Activate call-back function CmailSigHandlerCallBack()             */
2747     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2748
2749     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2750 }
2751
2752 void
2753 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2754 {
2755     BoardToTop();
2756     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
2757 }
2758 /**** end signal code ****/
2759
2760
2761 void
2762 ICSInitScript ()
2763 {
2764   /* try to open the icsLogon script, either in the location given
2765    * or in the users HOME directory
2766    */
2767
2768   FILE *f;
2769   char buf[MSG_SIZ];
2770   char *homedir;
2771
2772   f = fopen(appData.icsLogon, "r");
2773   if (f == NULL)
2774     {
2775       homedir = getenv("HOME");
2776       if (homedir != NULL)
2777         {
2778           safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2779           strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2780           strncat(buf, appData.icsLogon,  MSG_SIZ - strlen(buf) - 1);
2781           f = fopen(buf, "r");
2782         }
2783     }
2784
2785   if (f != NULL)
2786     ProcessICSInitScript(f);
2787   else
2788     printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2789
2790   return;
2791 }
2792
2793 void
2794 ResetFrontEnd ()
2795 {
2796     CommentPopDown();
2797     TagsPopDown();
2798     return;
2799 }
2800
2801 typedef struct {
2802     char *name;
2803     Boolean value;
2804 } Enables;
2805
2806 void
2807 GreyRevert (Boolean grey)
2808 {
2809     Widget w;
2810     if (!menuBarWidget) return;
2811     w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2812     if (w == NULL) {
2813       DisplayError("menuEdit.Revert", 0);
2814     } else {
2815       XtSetSensitive(w, !grey);
2816     }
2817     w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2818     if (w == NULL) {
2819       DisplayError("menuEdit.Annotate", 0);
2820     } else {
2821       XtSetSensitive(w, !grey);
2822     }
2823 }
2824
2825 void
2826 SetMenuEnables (Enables *enab)
2827 {
2828   Widget w;
2829   if (!menuBarWidget) return;
2830   while (enab->name != NULL) {
2831     w = XtNameToWidget(menuBarWidget, enab->name);
2832     if (w == NULL) {
2833       DisplayError(enab->name, 0);
2834     } else {
2835       XtSetSensitive(w, enab->value);
2836     }
2837     enab++;
2838   }
2839 }
2840
2841 Enables icsEnables[] = {
2842     { "menuFile.Mail Move", False },
2843     { "menuFile.Reload CMail Message", False },
2844     { "menuMode.Machine Black", False },
2845     { "menuMode.Machine White", False },
2846     { "menuMode.Analysis Mode", False },
2847     { "menuMode.Analyze File", False },
2848     { "menuMode.Two Machines", False },
2849     { "menuMode.Machine Match", False },
2850 #ifndef ZIPPY
2851     { "menuEngine.Hint", False },
2852     { "menuEngine.Book", False },
2853     { "menuEngine.Move Now", False },
2854 #ifndef OPTIONSDIALOG
2855     { "menuOptions.Periodic Updates", False },
2856     { "menuOptions.Hide Thinking", False },
2857     { "menuOptions.Ponder Next Move", False },
2858 #endif
2859 #endif
2860     { "menuEngine.Engine #1 Settings", False },
2861     { "menuEngine.Engine #2 Settings", False },
2862     { "menuEngine.Load Engine", False },
2863     { "menuEdit.Annotate", False },
2864     { "menuOptions.Match", False },
2865     { NULL, False }
2866 };
2867
2868 Enables ncpEnables[] = {
2869     { "menuFile.Mail Move", False },
2870     { "menuFile.Reload CMail Message", False },
2871     { "menuMode.Machine White", False },
2872     { "menuMode.Machine Black", False },
2873     { "menuMode.Analysis Mode", False },
2874     { "menuMode.Analyze File", False },
2875     { "menuMode.Two Machines", False },
2876     { "menuMode.Machine Match", False },
2877     { "menuMode.ICS Client", False },
2878     { "menuView.ICStex", False },
2879     { "menuView.ICS Input Box", False },
2880     { "Action", False },
2881     { "menuEdit.Revert", False },
2882     { "menuEdit.Annotate", False },
2883     { "menuEngine.Engine #1 Settings", False },
2884     { "menuEngine.Engine #2 Settings", False },
2885     { "menuEngine.Move Now", False },
2886     { "menuEngine.Retract Move", False },
2887     { "menuOptions.ICS", False },
2888 #ifndef OPTIONSDIALOG
2889     { "menuOptions.Auto Flag", False },
2890     { "menuOptions.Auto Flip View", False },
2891 //    { "menuOptions.ICS Alarm", False },
2892     { "menuOptions.Move Sound", False },
2893     { "menuOptions.Hide Thinking", False },
2894     { "menuOptions.Periodic Updates", False },
2895     { "menuOptions.Ponder Next Move", False },
2896 #endif
2897     { "menuEngine.Hint", False },
2898     { "menuEngine.Book", False },
2899     { NULL, False }
2900 };
2901
2902 Enables gnuEnables[] = {
2903     { "menuMode.ICS Client", False },
2904     { "menuView.ICStex", False },
2905     { "menuView.ICS Input Box", False },
2906     { "menuAction.Accept", False },
2907     { "menuAction.Decline", False },
2908     { "menuAction.Rematch", False },
2909     { "menuAction.Adjourn", False },
2910     { "menuAction.Stop Examining", False },
2911     { "menuAction.Stop Observing", False },
2912     { "menuAction.Upload to Examine", False },
2913     { "menuEdit.Revert", False },
2914     { "menuEdit.Annotate", False },
2915     { "menuOptions.ICS", False },
2916
2917     /* The next two options rely on SetCmailMode being called *after*    */
2918     /* SetGNUMode so that when GNU is being used to give hints these     */
2919     /* menu options are still available                                  */
2920
2921     { "menuFile.Mail Move", False },
2922     { "menuFile.Reload CMail Message", False },
2923     // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2924     { "menuMode.Machine White", True },
2925     { "menuMode.Machine Black", True },
2926     { "menuMode.Analysis Mode", True },
2927     { "menuMode.Analyze File", True },
2928     { "menuMode.Two Machines", True },
2929     { "menuMode.Machine Match", True },
2930     { "menuEngine.Engine #1 Settings", True },
2931     { "menuEngine.Engine #2 Settings", True },
2932     { "menuEngine.Hint", True },
2933     { "menuEngine.Book", True },
2934     { "menuEngine.Move Now", True },
2935     { "menuEngine.Retract Move", True },
2936     { "Action", True },
2937     { NULL, False }
2938 };
2939
2940 Enables cmailEnables[] = {
2941     { "Action", True },
2942     { "menuAction.Call Flag", False },
2943     { "menuAction.Draw", True },
2944     { "menuAction.Adjourn", False },
2945     { "menuAction.Abort", False },
2946     { "menuAction.Stop Observing", False },
2947     { "menuAction.Stop Examining", False },
2948     { "menuFile.Mail Move", True },
2949     { "menuFile.Reload CMail Message", True },
2950     { NULL, False }
2951 };
2952
2953 Enables trainingOnEnables[] = {
2954   { "menuMode.Edit Comment", False },
2955   { "menuMode.Pause", False },
2956   { "menuEdit.Forward", False },
2957   { "menuEdit.Backward", False },
2958   { "menuEdit.Forward to End", False },
2959   { "menuEdit.Back to Start", False },
2960   { "menuEngine.Move Now", False },
2961   { "menuEdit.Truncate Game", False },
2962   { NULL, False }
2963 };
2964
2965 Enables trainingOffEnables[] = {
2966   { "menuMode.Edit Comment", True },
2967   { "menuMode.Pause", True },
2968   { "menuEdit.Forward", True },
2969   { "menuEdit.Backward", True },
2970   { "menuEdit.Forward to End", True },
2971   { "menuEdit.Back to Start", True },
2972   { "menuEngine.Move Now", True },
2973   { "menuEdit.Truncate Game", True },
2974   { NULL, False }
2975 };
2976
2977 Enables machineThinkingEnables[] = {
2978   { "menuFile.Load Game", False },
2979 //  { "menuFile.Load Next Game", False },
2980 //  { "menuFile.Load Previous Game", False },
2981 //  { "menuFile.Reload Same Game", False },
2982   { "menuEdit.Paste Game", False },
2983   { "menuFile.Load Position", False },
2984 //  { "menuFile.Load Next Position", False },
2985 //  { "menuFile.Load Previous Position", False },
2986 //  { "menuFile.Reload Same Position", False },
2987   { "menuEdit.Paste Position", False },
2988   { "menuMode.Machine White", False },
2989   { "menuMode.Machine Black", False },
2990   { "menuMode.Two Machines", False },
2991 //  { "menuMode.Machine Match", False },
2992   { "menuEngine.Retract Move", False },
2993   { NULL, False }
2994 };
2995
2996 Enables userThinkingEnables[] = {
2997   { "menuFile.Load Game", True },
2998 //  { "menuFile.Load Next Game", True },
2999 //  { "menuFile.Load Previous Game", True },
3000 //  { "menuFile.Reload Same Game", True },
3001   { "menuEdit.Paste Game", True },
3002   { "menuFile.Load Position", True },
3003 //  { "menuFile.Load Next Position", True },
3004 //  { "menuFile.Load Previous Position", True },
3005 //  { "menuFile.Reload Same Position", True },
3006   { "menuEdit.Paste Position", True },
3007   { "menuMode.Machine White", True },
3008   { "menuMode.Machine Black", True },
3009   { "menuMode.Two Machines", True },
3010 //  { "menuMode.Machine Match", True },
3011   { "menuEngine.Retract Move", True },
3012   { NULL, False }
3013 };
3014
3015 void
3016 SetICSMode ()
3017 {
3018   SetMenuEnables(icsEnables);
3019
3020 #if ZIPPY
3021   if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3022      XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3023      XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3024   }
3025 #endif
3026 }
3027
3028 void
3029 SetNCPMode ()
3030 {
3031   SetMenuEnables(ncpEnables);
3032 }
3033
3034 void
3035 SetGNUMode ()
3036 {
3037   SetMenuEnables(gnuEnables);
3038 }
3039
3040 void
3041 SetCmailMode ()
3042 {
3043   SetMenuEnables(cmailEnables);
3044 }
3045
3046 void
3047 SetTrainingModeOn ()
3048 {
3049   SetMenuEnables(trainingOnEnables);
3050   if (appData.showButtonBar) {
3051     XtSetSensitive(buttonBarWidget, False);
3052   }
3053   CommentPopDown();
3054 }
3055
3056 void
3057 SetTrainingModeOff ()
3058 {
3059   SetMenuEnables(trainingOffEnables);
3060   if (appData.showButtonBar) {
3061     XtSetSensitive(buttonBarWidget, True);
3062   }
3063 }
3064
3065 void
3066 SetUserThinkingEnables ()
3067 {
3068   if (appData.noChessProgram) return;
3069   SetMenuEnables(userThinkingEnables);
3070 }
3071
3072 void
3073 SetMachineThinkingEnables ()
3074 {
3075   if (appData.noChessProgram) return;
3076   SetMenuEnables(machineThinkingEnables);
3077   switch (gameMode) {
3078   case MachinePlaysBlack:
3079   case MachinePlaysWhite:
3080   case TwoMachinesPlay:
3081     XtSetSensitive(XtNameToWidget(menuBarWidget,
3082                                   ModeToWidgetName(gameMode)), True);
3083     break;
3084   default:
3085     break;
3086   }
3087 }
3088
3089 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3090 #define HISTORY_SIZE 64
3091 static char *history[HISTORY_SIZE];
3092 int histIn = 0, histP = 0;
3093
3094 void
3095 SaveInHistory (char *cmd)
3096 {
3097   if (history[histIn] != NULL) {
3098     free(history[histIn]);
3099     history[histIn] = NULL;
3100   }
3101   if (*cmd == NULLCHAR) return;
3102   history[histIn] = StrSave(cmd);
3103   histIn = (histIn + 1) % HISTORY_SIZE;
3104   if (history[histIn] != NULL) {
3105     free(history[histIn]);
3106     history[histIn] = NULL;
3107   }
3108   histP = histIn;
3109 }
3110
3111 char *
3112 PrevInHistory (char *cmd)
3113 {
3114   int newhp;
3115   if (histP == histIn) {
3116     if (history[histIn] != NULL) free(history[histIn]);
3117     history[histIn] = StrSave(cmd);
3118   }
3119   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3120   if (newhp == histIn || history[newhp] == NULL) return NULL;
3121   histP = newhp;
3122   return history[histP];
3123 }
3124
3125 char *
3126 NextInHistory ()
3127 {
3128   if (histP == histIn) return NULL;
3129   histP = (histP + 1) % HISTORY_SIZE;
3130   return history[histP];   
3131 }
3132 // end of borrowed code
3133
3134 #define Abs(n) ((n)<0 ? -(n) : (n))
3135
3136 #ifdef ENABLE_NLS
3137 char *
3138 InsertPxlSize (char *pattern, int targetPxlSize)
3139 {
3140     char *base_fnt_lst, strInt[12], *p, *q;
3141     int alternatives, i, len, strIntLen;
3142
3143     /*
3144      * Replace the "*" (if present) in the pixel-size slot of each
3145      * alternative with the targetPxlSize.
3146      */
3147     p = pattern;
3148     alternatives = 1;
3149     while ((p = strchr(p, ',')) != NULL) {
3150       alternatives++;
3151       p++;
3152     }
3153     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3154     strIntLen = strlen(strInt);
3155     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3156
3157     p = pattern;
3158     q = base_fnt_lst;
3159     while (alternatives--) {
3160       char *comma = strchr(p, ',');
3161       for (i=0; i<14; i++) {
3162         char *hyphen = strchr(p, '-');
3163         if (!hyphen) break;
3164         if (comma && hyphen > comma) break;
3165         len = hyphen + 1 - p;
3166         if (i == 7 && *p == '*' && len == 2) {
3167           p += len;
3168           memcpy(q, strInt, strIntLen);
3169           q += strIntLen;
3170           *q++ = '-';
3171         } else {
3172           memcpy(q, p, len);
3173           p += len;
3174           q += len;
3175         }
3176       }
3177       if (!comma) break;
3178       len = comma + 1 - p;
3179       memcpy(q, p, len);
3180       p += len;
3181       q += len;
3182     }
3183     strcpy(q, p);
3184
3185     return base_fnt_lst;
3186 }
3187
3188 XFontSet
3189 CreateFontSet (char *base_fnt_lst)
3190 {
3191     XFontSet fntSet;
3192     char **missing_list;
3193     int missing_count;
3194     char *def_string;
3195
3196     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3197                             &missing_list, &missing_count, &def_string);
3198     if (appData.debugMode) {
3199       int i, count;
3200       XFontStruct **font_struct_list;
3201       char **font_name_list;
3202       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3203       if (fntSet) {
3204         fprintf(debugFP, " got list %s, locale %s\n",
3205                 XBaseFontNameListOfFontSet(fntSet),
3206                 XLocaleOfFontSet(fntSet));
3207         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3208         for (i = 0; i < count; i++) {
3209           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3210         }
3211       }
3212       for (i = 0; i < missing_count; i++) {
3213         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3214       }
3215     }
3216     if (fntSet == NULL) {
3217       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3218       exit(2);
3219     }
3220     return fntSet;
3221 }
3222 #else // not ENABLE_NLS
3223 /*
3224  * Find a font that matches "pattern" that is as close as
3225  * possible to the targetPxlSize.  Prefer fonts that are k
3226  * pixels smaller to fonts that are k pixels larger.  The
3227  * pattern must be in the X Consortium standard format,
3228  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3229  * The return value should be freed with XtFree when no
3230  * longer needed.
3231  */
3232 char *
3233 FindFont (char *pattern, int targetPxlSize)
3234 {
3235     char **fonts, *p, *best, *scalable, *scalableTail;
3236     int i, j, nfonts, minerr, err, pxlSize;
3237
3238     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3239     if (nfonts < 1) {
3240         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3241                 programName, pattern);
3242         exit(2);
3243     }
3244
3245     best = fonts[0];
3246     scalable = NULL;
3247     minerr = 999999;
3248     for (i=0; i<nfonts; i++) {
3249         j = 0;
3250         p = fonts[i];
3251         if (*p != '-') continue;
3252         while (j < 7) {
3253             if (*p == NULLCHAR) break;
3254             if (*p++ == '-') j++;
3255         }
3256         if (j < 7) continue;
3257         pxlSize = atoi(p);
3258         if (pxlSize == 0) {
3259             scalable = fonts[i];
3260             scalableTail = p;
3261         } else {
3262             err = pxlSize - targetPxlSize;
3263             if (Abs(err) < Abs(minerr) ||
3264                 (minerr > 0 && err < 0 && -err == minerr)) {
3265                 best = fonts[i];
3266                 minerr = err;
3267             }
3268         }
3269     }
3270     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3271         /* If the error is too big and there is a scalable font,
3272            use the scalable font. */
3273         int headlen = scalableTail - scalable;
3274         p = (char *) XtMalloc(strlen(scalable) + 10);
3275         while (isdigit(*scalableTail)) scalableTail++;
3276         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3277     } else {
3278         p = (char *) XtMalloc(strlen(best) + 2);
3279         safeStrCpy(p, best, strlen(best)+1 );
3280     }
3281     if (appData.debugMode) {
3282         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
3283                 pattern, targetPxlSize, p);
3284     }
3285     XFreeFontNames(fonts);
3286     return p;
3287 }
3288 #endif
3289
3290 void
3291 DeleteGCs ()
3292 {   // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3293     // must be called before all non-first callse to CreateGCs()
3294     XtReleaseGC(shellWidget, highlineGC);
3295     XtReleaseGC(shellWidget, lightSquareGC);
3296     XtReleaseGC(shellWidget, darkSquareGC);
3297     XtReleaseGC(shellWidget, lineGC);
3298     if (appData.monoMode) {
3299         if (DefaultDepth(xDisplay, xScreen) == 1) {
3300             XtReleaseGC(shellWidget, wbPieceGC);
3301         } else {
3302             XtReleaseGC(shellWidget, bwPieceGC);
3303         }
3304     } else {
3305         XtReleaseGC(shellWidget, prelineGC);
3306         XtReleaseGC(shellWidget, jailSquareGC);
3307         XtReleaseGC(shellWidget, wdPieceGC);
3308         XtReleaseGC(shellWidget, wlPieceGC);
3309         XtReleaseGC(shellWidget, wjPieceGC);
3310         XtReleaseGC(shellWidget, bdPieceGC);
3311         XtReleaseGC(shellWidget, blPieceGC);
3312         XtReleaseGC(shellWidget, bjPieceGC);
3313     }
3314 }
3315
3316 void
3317 CreateGCs (int redo)
3318 {
3319     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3320       | GCBackground | GCFunction | GCPlaneMask;
3321     XGCValues gc_values;
3322     GC copyInvertedGC;
3323
3324     gc_values.plane_mask = AllPlanes;
3325     gc_values.line_width = lineGap;
3326     gc_values.line_style = LineSolid;
3327     gc_values.function = GXcopy;
3328
3329   if(redo) {
3330     DeleteGCs(); // called a second time; clean up old GCs first
3331   } else { // [HGM] grid and font GCs created on first call only
3332     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3333     gc_values.background = XWhitePixel(xDisplay, xScreen);
3334     coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3335     XSetFont(xDisplay, coordGC, coordFontID);
3336
3337     // [HGM] make font for holdings counts (white on black)
3338     gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3339     gc_values.background = XBlackPixel(xDisplay, xScreen);
3340     countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3341     XSetFont(xDisplay, countGC, countFontID);
3342   }
3343     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3344     gc_values.background = XBlackPixel(xDisplay, xScreen);
3345     lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3346
3347     if (appData.monoMode) {
3348         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3349         gc_values.background = XWhitePixel(xDisplay, xScreen);
3350         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3351
3352         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3353         gc_values.background = XBlackPixel(xDisplay, xScreen);
3354         lightSquareGC = wbPieceGC
3355           = XtGetGC(shellWidget, value_mask, &gc_values);
3356
3357         gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3358         gc_values.background = XWhitePixel(xDisplay, xScreen);
3359         darkSquareGC = bwPieceGC
3360           = XtGetGC(shellWidget, value_mask, &gc_values);
3361
3362         if (DefaultDepth(xDisplay, xScreen) == 1) {
3363             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3364             gc_values.function = GXcopyInverted;
3365             copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3366             gc_values.function = GXcopy;
3367             if (XBlackPixel(xDisplay, xScreen) == 1) {
3368                 bwPieceGC = darkSquareGC;
3369                 wbPieceGC = copyInvertedGC;
3370             } else {
3371                 bwPieceGC = copyInvertedGC;
3372                 wbPieceGC = lightSquareGC;
3373             }
3374         }
3375     } else {
3376         gc_values.foreground = highlightSquareColor;
3377         gc_values.background = highlightSquareColor;
3378         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3379
3380         gc_values.foreground = premoveHighlightColor;
3381         gc_values.background = premoveHighlightColor;
3382         prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3383
3384         gc_values.foreground = lightSquareColor;
3385         gc_values.background = darkSquareColor;
3386         lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3387
3388         gc_values.foreground = darkSquareColor;
3389         gc_values.background = lightSquareColor;
3390         darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3391
3392         gc_values.foreground = jailSquareColor;
3393         gc_values.background = jailSquareColor;
3394         jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3395
3396         gc_values.foreground = whitePieceColor;
3397         gc_values.background = darkSquareColor;
3398         wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3399
3400         gc_values.foreground = whitePieceColor;
3401         gc_values.background = lightSquareColor;
3402         wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3403
3404         gc_values.foreground = whitePieceColor;
3405         gc_values.background = jailSquareColor;
3406         wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3407
3408         gc_values.foreground = blackPieceColor;
3409         gc_values.background = darkSquareColor;
3410         bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3411
3412         gc_values.foreground = blackPieceColor;
3413         gc_values.background = lightSquareColor;
3414         blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3415
3416         gc_values.foreground = blackPieceColor;
3417         gc_values.background = jailSquareColor;
3418         bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3419     }
3420 }
3421
3422 void
3423 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
3424 {
3425     int x, y, w, h, p;
3426     FILE *fp;
3427     Pixmap temp;
3428     XGCValues   values;
3429     GC maskGC;
3430
3431     fp = fopen(filename, "rb");
3432     if (!fp) {
3433         fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3434         exit(1);
3435     }
3436
3437     w = fgetc(fp);
3438     h = fgetc(fp);
3439
3440     for (y=0; y<h; ++y) {
3441         for (x=0; x<h; ++x) {
3442             p = fgetc(fp);
3443
3444             switch (p) {
3445               case 0:
3446                 XPutPixel(xim, x, y, blackPieceColor);
3447                 if (xmask)
3448                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3449                 break;
3450               case 1:
3451                 XPutPixel(xim, x, y, darkSquareColor);
3452                 if (xmask)
3453                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3454                 break;
3455               case 2:
3456                 XPutPixel(xim, x, y, whitePieceColor);
3457                 if (xmask)
3458                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3459                 break;
3460               case 3:
3461                 XPutPixel(xim, x, y, lightSquareColor);
3462                 if (xmask)
3463                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3464                 break;
3465             }
3466         }
3467     }
3468
3469     fclose(fp);
3470
3471     /* create Pixmap of piece */
3472     *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3473                           w, h, xim->depth);
3474     XPutImage(xDisplay, *dest, lightSquareGC, xim,
3475               0, 0, 0, 0, w, h);
3476
3477     /* create Pixmap of clipmask
3478        Note: We assume the white/black pieces have the same
3479              outline, so we make only 6 masks. This is okay
3480              since the XPM clipmask routines do the same. */
3481     if (xmask) {
3482       temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3483                             w, h, xim->depth);
3484       XPutImage(xDisplay, temp, lightSquareGC, xmask,
3485               0, 0, 0, 0, w, h);
3486
3487       /* now create the 1-bit version */
3488       *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3489                           w, h, 1);
3490
3491       values.foreground = 1;
3492       values.background = 0;
3493
3494       /* Don't use XtGetGC, not read only */
3495       maskGC = XCreateGC(xDisplay, *mask,
3496                     GCForeground | GCBackground, &values);
3497       XCopyPlane(xDisplay, temp, *mask, maskGC,
3498                   0, 0, squareSize, squareSize, 0, 0, 1);
3499       XFreePixmap(xDisplay, temp);
3500     }
3501 }
3502
3503
3504 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3505
3506 void
3507 CreateXIMPieces ()
3508 {
3509     int piece, kind;
3510     char buf[MSG_SIZ];
3511     u_int ss;
3512     static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3513     XImage *ximtemp;
3514
3515     ss = squareSize;
3516
3517     /* The XSynchronize calls were copied from CreatePieces.
3518        Not sure if needed, but can't hurt */
3519     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3520                                      buffering bug */
3521
3522     /* temp needed by loadXIM() */
3523     ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3524                  0, 0, ss, ss, AllPlanes, XYPixmap);
3525
3526     if (strlen(appData.pixmapDirectory) == 0) {
3527       useImages = 0;
3528     } else {
3529         useImages = 1;
3530         if (appData.monoMode) {
3531           DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3532                             0, 2);
3533           ExitEvent(2);
3534         }
3535         fprintf(stderr, _("\nLoading XIMs...\n"));
3536         /* Load pieces */
3537         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3538             fprintf(stderr, "%d", piece+1);
3539             for (kind=0; kind<4; kind++) {
3540                 fprintf(stderr, ".");
3541                 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3542                         ExpandPathName(appData.pixmapDirectory),
3543                         piece <= (int) WhiteKing ? "" : "w",
3544                         pieceBitmapNames[piece],
3545                         ximkind[kind], ss);
3546                 ximPieceBitmap[kind][piece] =
3547                   XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3548                             0, 0, ss, ss, AllPlanes, XYPixmap);
3549                 if (appData.debugMode)
3550                   fprintf(stderr, _("(File:%s:) "), buf);
3551                 loadXIM(ximPieceBitmap[kind][piece],
3552                         ximtemp, buf,
3553                         &(xpmPieceBitmap2[kind][piece]),
3554                         &(ximMaskPm2[piece]));
3555                 if(piece <= (int)WhiteKing)
3556                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3557             }
3558             fprintf(stderr," ");
3559         }
3560         /* Load light and dark squares */
3561         /* If the LSQ and DSQ pieces don't exist, we will
3562            draw them with solid squares. */
3563         snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3564         if (access(buf, 0) != 0) {
3565             useImageSqs = 0;
3566         } else {
3567             useImageSqs = 1;
3568             fprintf(stderr, _("light square "));
3569             ximLightSquare=
3570               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3571                         0, 0, ss, ss, AllPlanes, XYPixmap);
3572             if (appData.debugMode)
3573               fprintf(stderr, _("(File:%s:) "), buf);
3574
3575             loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3576             fprintf(stderr, _("dark square "));
3577             snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3578                     ExpandPathName(appData.pixmapDirectory), ss);
3579             if (appData.debugMode)
3580               fprintf(stderr, _("(File:%s:) "), buf);
3581             ximDarkSquare=
3582               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3583                         0, 0, ss, ss, AllPlanes, XYPixmap);
3584             loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3585             xpmJailSquare = xpmLightSquare;
3586         }
3587         fprintf(stderr, _("Done.\n"));
3588     }
3589     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3590 }
3591
3592 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3593
3594 #if HAVE_LIBXPM
3595 void
3596 CreateXPMBoard (char *s, int kind)
3597 {
3598     XpmAttributes attr;
3599     attr.valuemask = 0;
3600     if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3601     if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3602         useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3603     }
3604 }
3605
3606 void
3607 FreeXPMPieces ()
3608 {   // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3609     // thisroutine has to be called t free the old piece pixmaps
3610     int piece, kind;
3611     for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3612         for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3613     if(useImageSqs) {
3614         XFreePixmap(xDisplay, xpmLightSquare);
3615         XFreePixmap(xDisplay, xpmDarkSquare);
3616     }
3617 }
3618
3619 void
3620 CreateXPMPieces ()
3621 {
3622     int piece, kind, r;
3623     char buf[MSG_SIZ];
3624     u_int ss = squareSize;
3625     XpmAttributes attr;
3626     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3627     XpmColorSymbol symbols[4];
3628     static int redo = False;
3629
3630     if(redo) FreeXPMPieces(); else redo = 1;
3631
3632     /* The XSynchronize calls were copied from CreatePieces.
3633        Not sure if needed, but can't hurt */
3634     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3635
3636     /* Setup translations so piece colors match square colors */
3637     symbols[0].name = "light_piece";
3638     symbols[0].value = appData.whitePieceColor;
3639     symbols[1].name = "dark_piece";
3640     symbols[1].value = appData.blackPieceColor;
3641     symbols[2].name = "light_square";
3642     symbols[2].value = appData.lightSquareColor;
3643     symbols[3].name = "dark_square";
3644     symbols[3].value = appData.darkSquareColor;
3645
3646     attr.valuemask = XpmColorSymbols;
3647     attr.colorsymbols&nbs