c2a53737a82a80b7a330f84ef798485216b438d2
[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
1559 void
1560 GetActualPlacement (Widget wg, WindowPlacement *wp)
1561 {
1562   Arg args[16];
1563   Dimension w, h;
1564   Position x, y;
1565   int i;
1566
1567   if(!wg) return;
1568
1569     i = 0;
1570     XtSetArg(args[i], XtNx, &x); i++;
1571     XtSetArg(args[i], XtNy, &y); i++;
1572     XtSetArg(args[i], XtNwidth, &w); i++;
1573     XtSetArg(args[i], XtNheight, &h); i++;
1574     XtGetValues(wg, args, i);
1575     wp->x = x - 4;
1576     wp->y = y - 23;
1577     wp->height = h;
1578     wp->width = w;
1579 }
1580
1581 void
1582 GetWindowCoords ()
1583 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1584   // In XBoard this will have to wait until awareness of window parameters is implemented
1585   GetActualPlacement(shellWidget, &wpMain);
1586   if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1587   if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1588   if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1589   if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1590   if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1591   if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1592 }
1593
1594 void
1595 PrintCommPortSettings (FILE *f, char *name)
1596 { // This option does not exist in XBoard
1597 }
1598
1599 int
1600 MySearchPath (char *installDir, char *name, char *fullname)
1601 { // just append installDir and name. Perhaps ExpandPath should be used here?
1602   name = ExpandPathName(name);
1603   if(name && name[0] == '/')
1604     safeStrCpy(fullname, name, MSG_SIZ );
1605   else {
1606     sprintf(fullname, "%s%c%s", installDir, '/', name);
1607   }
1608   return 1;
1609 }
1610
1611 int
1612 MyGetFullPathName (char *name, char *fullname)
1613 { // should use ExpandPath?
1614   name = ExpandPathName(name);
1615   safeStrCpy(fullname, name, MSG_SIZ );
1616   return 1;
1617 }
1618
1619 void
1620 EnsureOnScreen (int *x, int *y, int minX, int minY)
1621 {
1622   return;
1623 }
1624
1625 int
1626 MainWindowUp ()
1627 { // [HGM] args: allows testing if main window is realized from back-end
1628   return xBoardWindow != 0;
1629 }
1630
1631 void
1632 PopUpStartupDialog ()
1633 {  // start menu not implemented in XBoard
1634 }
1635
1636 char *
1637 ConvertToLine (int argc, char **argv)
1638 {
1639   static char line[128*1024], buf[1024];
1640   int i;
1641
1642   line[0] = NULLCHAR;
1643   for(i=1; i<argc; i++)
1644     {
1645       if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1646           && argv[i][0] != '{' )
1647         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1648       else
1649         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1650       strncat(line, buf, 128*1024 - strlen(line) - 1 );
1651     }
1652
1653   line[strlen(line)-1] = NULLCHAR;
1654   return line;
1655 }
1656
1657 //--------------------------------------------------------------------------------------------
1658
1659 extern Boolean twoBoards, partnerUp;
1660
1661 #ifdef IDSIZES
1662   // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1663 #else
1664 #define BoardSize int
1665 void
1666 InitDrawingSizes (BoardSize boardSize, int flags)
1667 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1668     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1669     Arg args[16];
1670     XtGeometryResult gres;
1671     int i;
1672     static Dimension oldWidth, oldHeight;
1673     static VariantClass oldVariant;
1674     static int oldDual = -1, oldMono = -1;
1675
1676     if(!formWidget) return;
1677
1678     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1679     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1680     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1681
1682   if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1683     /*
1684      * Enable shell resizing.
1685      */
1686     shellArgs[0].value = (XtArgVal) &w;
1687     shellArgs[1].value = (XtArgVal) &h;
1688     XtGetValues(shellWidget, shellArgs, 2);
1689
1690     shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1691     shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1692     XtSetValues(shellWidget, &shellArgs[2], 4);
1693
1694     XtSetArg(args[0], XtNdefaultDistance, &sep);
1695     XtGetValues(formWidget, args, 1);
1696
1697     oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1698     CreateGrid();
1699     hOffset = boardWidth + 10;
1700     for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1701         secondSegments[i] = gridSegments[i];
1702         secondSegments[i].x1 += hOffset;
1703         secondSegments[i].x2 += hOffset;
1704     }
1705
1706     XtSetArg(args[0], XtNwidth, boardWidth);
1707     XtSetArg(args[1], XtNheight, boardHeight);
1708     XtSetValues(boardWidget, args, 2);
1709
1710     timerWidth = (boardWidth - sep) / 2;
1711     XtSetArg(args[0], XtNwidth, timerWidth);
1712     XtSetValues(whiteTimerWidget, args, 1);
1713     XtSetValues(blackTimerWidget, args, 1);
1714
1715     XawFormDoLayout(formWidget, False);
1716
1717     if (appData.titleInWindow) {
1718         i = 0;
1719         XtSetArg(args[i], XtNborderWidth, &bor); i++;
1720         XtSetArg(args[i], XtNheight, &h);  i++;
1721         XtGetValues(titleWidget, args, i);
1722         if (smallLayout) {
1723             w = boardWidth - 2*bor;
1724         } else {
1725             XtSetArg(args[0], XtNwidth, &w);
1726             XtGetValues(menuBarWidget, args, 1);
1727             w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1728         }
1729
1730         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1731         if (gres != XtGeometryYes && appData.debugMode) {
1732             fprintf(stderr,
1733                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1734                     programName, gres, w, h, wr, hr);
1735         }
1736     }
1737
1738     XawFormDoLayout(formWidget, True);
1739
1740     /*
1741      * Inhibit shell resizing.
1742      */
1743     shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1744     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1745     shellArgs[4].value = shellArgs[2].value = w;
1746     shellArgs[5].value = shellArgs[3].value = h;
1747     XtSetValues(shellWidget, &shellArgs[0], 6);
1748   }
1749
1750     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1751     // (only for xpm)
1752
1753   if(gameInfo.variant != oldVariant) { // and only if variant changed
1754
1755     if(useImages) {
1756       for(i=0; i<4; i++) {
1757         int p;
1758         for(p=0; p<=(int)WhiteKing; p++)
1759            xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1760         if(gameInfo.variant == VariantShogi) {
1761            xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1762            xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1763            xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1764            xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1765            xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1766         }
1767 #ifdef GOTHIC
1768         if(gameInfo.variant == VariantGothic) {
1769            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1770         }
1771 #endif
1772         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1773            xpmPieceBitmap[i][(int)WhiteAngel]    = xpmPieceBitmap2[i][(int)WhiteFalcon];
1774            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1775         }
1776 #if !HAVE_LIBXPM
1777         // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1778         for(p=0; p<=(int)WhiteKing; p++)
1779            ximMaskPm[p] = ximMaskPm2[p]; // defaults
1780         if(gameInfo.variant == VariantShogi) {
1781            ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1782            ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1783            ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1784            ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1785            ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1786         }
1787 #ifdef GOTHIC
1788         if(gameInfo.variant == VariantGothic) {
1789            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1790         }
1791 #endif
1792         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1793            ximMaskPm[(int)WhiteAngel]    = ximMaskPm2[(int)WhiteFalcon];
1794            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1795         }
1796 #endif
1797       }
1798     } else {
1799       for(i=0; i<2; i++) {
1800         int p;
1801         for(p=0; p<=(int)WhiteKing; p++)
1802            pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1803         if(gameInfo.variant == VariantShogi) {
1804            pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1805            pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1806            pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1807            pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1808            pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1809         }
1810 #ifdef GOTHIC
1811         if(gameInfo.variant == VariantGothic) {
1812            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1813         }
1814 #endif
1815         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1816            pieceBitmap[i][(int)WhiteAngel]    = pieceBitmap2[i][(int)WhiteFalcon];
1817            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1818         }
1819       }
1820     }
1821     oldMono = -10; // kludge to force recreation of animation masks
1822   }
1823 #if HAVE_LIBXPM
1824   if(appData.monoMode != oldMono)
1825     CreateAnimVars();
1826 #endif
1827   oldMono = appData.monoMode;
1828 }
1829 #endif
1830
1831 void
1832 ParseIcsTextColors ()
1833 {   // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1834     if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1835         parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1836         parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
1837         parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
1838         parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1839         parse_cpair(ColorTell, appData.colorTell) < 0 ||
1840         parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
1841         parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
1842         parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
1843         parse_cpair(ColorNormal, appData.colorNormal) < 0)
1844       {
1845           if (appData.colorize) {
1846               fprintf(stderr,
1847                       _("%s: can't parse color names; disabling colorization\n"),
1848                       programName);
1849           }
1850           appData.colorize = FALSE;
1851       }
1852 }
1853
1854 int
1855 MakeColors ()
1856 {   // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1857     XrmValue vFrom, vTo;
1858     int forceMono = False;
1859
1860     if (!appData.monoMode) {
1861         vFrom.addr = (caddr_t) appData.lightSquareColor;
1862         vFrom.size = strlen(appData.lightSquareColor);
1863         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1864         if (vTo.addr == NULL) {
1865           appData.monoMode = True;
1866           forceMono = True;
1867         } else {
1868           lightSquareColor = *(Pixel *) vTo.addr;
1869         }
1870     }
1871     if (!appData.monoMode) {
1872         vFrom.addr = (caddr_t) appData.darkSquareColor;
1873         vFrom.size = strlen(appData.darkSquareColor);
1874         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1875         if (vTo.addr == NULL) {
1876           appData.monoMode = True;
1877           forceMono = True;
1878         } else {
1879           darkSquareColor = *(Pixel *) vTo.addr;
1880         }
1881     }
1882     if (!appData.monoMode) {
1883         vFrom.addr = (caddr_t) appData.whitePieceColor;
1884         vFrom.size = strlen(appData.whitePieceColor);
1885         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1886         if (vTo.addr == NULL) {
1887           appData.monoMode = True;
1888           forceMono = True;
1889         } else {
1890           whitePieceColor = *(Pixel *) vTo.addr;
1891         }
1892     }
1893     if (!appData.monoMode) {
1894         vFrom.addr = (caddr_t) appData.blackPieceColor;
1895         vFrom.size = strlen(appData.blackPieceColor);
1896         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1897         if (vTo.addr == NULL) {
1898           appData.monoMode = True;
1899           forceMono = True;
1900         } else {
1901           blackPieceColor = *(Pixel *) vTo.addr;
1902         }
1903     }
1904
1905     if (!appData.monoMode) {
1906         vFrom.addr = (caddr_t) appData.highlightSquareColor;
1907         vFrom.size = strlen(appData.highlightSquareColor);
1908         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1909         if (vTo.addr == NULL) {
1910           appData.monoMode = True;
1911           forceMono = True;
1912         } else {
1913           highlightSquareColor = *(Pixel *) vTo.addr;
1914         }
1915     }
1916
1917     if (!appData.monoMode) {
1918         vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1919         vFrom.size = strlen(appData.premoveHighlightColor);
1920         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1921         if (vTo.addr == NULL) {
1922           appData.monoMode = True;
1923           forceMono = True;
1924         } else {
1925           premoveHighlightColor = *(Pixel *) vTo.addr;
1926         }
1927     }
1928     return forceMono;
1929 }
1930
1931 void
1932 CreateAnyPieces ()
1933 {   // [HGM] taken out of main
1934 #if HAVE_LIBXPM
1935     if (appData.monoMode && // [HGM] no sense to go on to certain doom
1936        (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1937             appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1938
1939     if (appData.bitmapDirectory[0] != NULLCHAR) {
1940       CreatePieces();
1941     } else {
1942       CreateXPMPieces();
1943       CreateXPMBoard(appData.liteBackTextureFile, 1);
1944       CreateXPMBoard(appData.darkBackTextureFile, 0);
1945     }
1946 #else
1947     CreateXIMPieces();
1948     /* Create regular pieces */
1949     if (!useImages) CreatePieces();
1950 #endif
1951 }
1952
1953 int
1954 main (int argc, char **argv)
1955 {
1956     int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1957     XSetWindowAttributes window_attributes;
1958     Arg args[16];
1959     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1960     XrmValue vFrom, vTo;
1961     XtGeometryResult gres;
1962     char *p;
1963     XrmDatabase xdb;
1964     int forceMono = False;
1965
1966     srandom(time(0)); // [HGM] book: make random truly random
1967
1968     setbuf(stdout, NULL);
1969     setbuf(stderr, NULL);
1970     debugFP = stderr;
1971
1972     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1973         printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1974         exit(0);
1975     }
1976
1977     programName = strrchr(argv[0], '/');
1978     if (programName == NULL)
1979       programName = argv[0];
1980     else
1981       programName++;
1982
1983 #ifdef ENABLE_NLS
1984     XtSetLanguageProc(NULL, NULL, NULL);
1985     bindtextdomain(PACKAGE, LOCALEDIR);
1986     textdomain(PACKAGE);
1987 #endif
1988
1989     shellWidget =
1990       XtAppInitialize(&appContext, "XBoard", shellOptions,
1991                       XtNumber(shellOptions),
1992                       &argc, argv, xboardResources, NULL, 0);
1993     appData.boardSize = "";
1994     InitAppData(ConvertToLine(argc, argv));
1995     p = getenv("HOME");
1996     if (p == NULL) p = "/tmp";
1997     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1998     gameCopyFilename = (char*) malloc(i);
1999     gamePasteFilename = (char*) malloc(i);
2000     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
2001     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2002
2003     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2004                               clientResources, XtNumber(clientResources),
2005                               NULL, 0);
2006
2007     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2008         static char buf[MSG_SIZ];
2009         EscapeExpand(buf, appData.firstInitString);
2010         appData.firstInitString = strdup(buf);
2011         EscapeExpand(buf, appData.secondInitString);
2012         appData.secondInitString = strdup(buf);
2013         EscapeExpand(buf, appData.firstComputerString);
2014         appData.firstComputerString = strdup(buf);
2015         EscapeExpand(buf, appData.secondComputerString);
2016         appData.secondComputerString = strdup(buf);
2017     }
2018
2019     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2020         chessDir = ".";
2021     } else {
2022         if (chdir(chessDir) != 0) {
2023             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2024             perror(chessDir);
2025             exit(1);
2026         }
2027     }
2028
2029     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2030         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2031         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
2032            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2033            exit(errno);
2034         }
2035         setbuf(debugFP, NULL);
2036     }
2037
2038 #if ENABLE_NLS
2039     if (appData.debugMode) {
2040       fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2041     }
2042 #endif
2043
2044     /* [HGM,HR] make sure board size is acceptable */
2045     if(appData.NrFiles > BOARD_FILES ||
2046        appData.NrRanks > BOARD_RANKS   )
2047          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2048
2049 #if !HIGHDRAG
2050     /* This feature does not work; animation needs a rewrite */
2051     appData.highlightDragging = FALSE;
2052 #endif
2053     InitBackEnd1();
2054
2055     xDisplay = XtDisplay(shellWidget);
2056     xScreen = DefaultScreen(xDisplay);
2057     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2058
2059         gameInfo.variant = StringToVariant(appData.variant);
2060         InitPosition(FALSE);
2061
2062 #ifdef IDSIZE
2063     InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2064 #else
2065     if (isdigit(appData.boardSize[0])) {
2066         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2067                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2068                    &fontPxlSize, &smallLayout, &tinyLayout);
2069         if (i == 0) {
2070             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2071                     programName, appData.boardSize);
2072             exit(2);
2073         }
2074         if (i < 7) {
2075             /* Find some defaults; use the nearest known size */
2076             SizeDefaults *szd, *nearest;
2077             int distance = 99999;
2078             nearest = szd = sizeDefaults;
2079             while (szd->name != NULL) {
2080                 if (abs(szd->squareSize - squareSize) < distance) {
2081                     nearest = szd;
2082                     distance = abs(szd->squareSize - squareSize);
2083                     if (distance == 0) break;
2084                 }
2085                 szd++;
2086             }
2087             if (i < 2) lineGap = nearest->lineGap;
2088             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2089             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2090             if (i < 5) fontPxlSize = nearest->fontPxlSize;
2091             if (i < 6) smallLayout = nearest->smallLayout;
2092             if (i < 7) tinyLayout = nearest->tinyLayout;
2093         }
2094     } else {
2095         SizeDefaults *szd = sizeDefaults;
2096         if (*appData.boardSize == NULLCHAR) {
2097             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2098                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2099               szd++;
2100             }
2101             if (szd->name == NULL) szd--;
2102             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2103         } else {
2104             while (szd->name != NULL &&
2105                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2106             if (szd->name == NULL) {
2107                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2108                         programName, appData.boardSize);
2109                 exit(2);
2110             }
2111         }
2112         squareSize = szd->squareSize;
2113         lineGap = szd->lineGap;
2114         clockFontPxlSize = szd->clockFontPxlSize;
2115         coordFontPxlSize = szd->coordFontPxlSize;
2116         fontPxlSize = szd->fontPxlSize;
2117         smallLayout = szd->smallLayout;
2118         tinyLayout = szd->tinyLayout;
2119         // [HGM] font: use defaults from settings file if available and not overruled
2120     }
2121     if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2122         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2123     if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2124         appData.font = fontTable[MESSAGE_FONT][squareSize];
2125     if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2126         appData.coordFont = fontTable[COORD_FONT][squareSize];
2127
2128     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2129     if (strlen(appData.pixmapDirectory) > 0) {
2130         p = ExpandPathName(appData.pixmapDirectory);
2131         if (!p) {
2132             fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2133                    appData.pixmapDirectory);
2134             exit(1);
2135         }
2136         if (appData.debugMode) {
2137           fprintf(stderr, _("\
2138 XBoard square size (hint): %d\n\
2139 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2140         }
2141         squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2142         if (appData.debugMode) {
2143             fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2144         }
2145     }
2146     defaultLineGap = lineGap;
2147     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2148
2149     /* [HR] height treated separately (hacked) */
2150     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2151     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2152     if (appData.showJail == 1) {
2153         /* Jail on top and bottom */
2154         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2155         XtSetArg(boardArgs[2], XtNheight,
2156                  boardHeight + 2*(lineGap + squareSize));
2157     } else if (appData.showJail == 2) {
2158         /* Jail on sides */
2159         XtSetArg(boardArgs[1], XtNwidth,
2160                  boardWidth + 2*(lineGap + squareSize));
2161         XtSetArg(boardArgs[2], XtNheight, boardHeight);
2162     } else {
2163         /* No jail */
2164         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2165         XtSetArg(boardArgs[2], XtNheight, boardHeight);
2166     }
2167
2168     /*
2169      * Determine what fonts to use.
2170      */
2171 #if ENABLE_NLS
2172     appData.font = InsertPxlSize(appData.font, fontPxlSize);
2173     appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2174     appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2175     fontSet = CreateFontSet(appData.font);
2176     clockFontSet = CreateFontSet(appData.clockFont);
2177     {
2178       /* For the coordFont, use the 0th font of the fontset. */
2179       XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2180       XFontStruct **font_struct_list;
2181       XFontSetExtents *fontSize;
2182       char **font_name_list;
2183       XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2184       coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2185       coordFontStruct = XQueryFont(xDisplay, coordFontID);
2186       fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
2187       textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
2188     }
2189 #else
2190     appData.font = FindFont(appData.font, fontPxlSize);
2191     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2192     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2193     clockFontID = XLoadFont(xDisplay, appData.clockFont);
2194     clockFontStruct = XQueryFont(xDisplay, clockFontID);
2195     coordFontID = XLoadFont(xDisplay, appData.coordFont);
2196     coordFontStruct = XQueryFont(xDisplay, coordFontID);
2197 #endif
2198     countFontID = coordFontID;  // [HGM] holdings
2199     countFontStruct = coordFontStruct;
2200
2201     xdb = XtDatabase(xDisplay);
2202 #if ENABLE_NLS
2203     XrmPutLineResource(&xdb, "*international: True");
2204     vTo.size = sizeof(XFontSet);
2205     vTo.addr = (XtPointer) &fontSet;
2206     XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2207 #else
2208     XrmPutStringResource(&xdb, "*font", appData.font);
2209 #endif
2210
2211     /*
2212      * Detect if there are not enough colors available and adapt.
2213      */
2214     if (DefaultDepth(xDisplay, xScreen) <= 2) {
2215       appData.monoMode = True;
2216     }
2217
2218     forceMono = MakeColors();
2219
2220     if (forceMono) {
2221       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2222               programName);
2223         appData.monoMode = True;
2224     }
2225
2226     if (appData.lowTimeWarning && !appData.monoMode) {
2227       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2228       vFrom.size = strlen(appData.lowTimeWarningColor);
2229       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2230       if (vTo.addr == NULL)
2231                 appData.monoMode = True;
2232       else
2233                 lowTimeWarningColor = *(Pixel *) vTo.addr;
2234     }
2235
2236     if (appData.monoMode && appData.debugMode) {
2237         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2238                 (unsigned long) XWhitePixel(xDisplay, xScreen),
2239                 (unsigned long) XBlackPixel(xDisplay, xScreen));
2240     }
2241
2242     ParseIcsTextColors();
2243     textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2244     textColors[ColorNone].attr = 0;
2245
2246     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2247
2248     /*
2249      * widget hierarchy
2250      */
2251     if (tinyLayout) {
2252         layoutName = "tinyLayout";
2253     } else if (smallLayout) {
2254         layoutName = "smallLayout";
2255     } else {
2256         layoutName = "normalLayout";
2257     }
2258     /* Outer layoutWidget is there only to provide a name for use in
2259        resources that depend on the layout style */
2260     layoutWidget =
2261       XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2262                             layoutArgs, XtNumber(layoutArgs));
2263     formWidget =
2264       XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2265                             formArgs, XtNumber(formArgs));
2266     XtSetArg(args[0], XtNdefaultDistance, &sep);
2267     XtGetValues(formWidget, args, 1);
2268
2269     j = 0;
2270     widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
2271     XtSetArg(args[0], XtNtop,    XtChainTop);
2272     XtSetArg(args[1], XtNbottom, XtChainTop);
2273     XtSetArg(args[2], XtNright,  XtChainLeft);
2274     XtSetValues(menuBarWidget, args, 3);
2275
2276     widgetList[j++] = whiteTimerWidget =
2277       XtCreateWidget("whiteTime", labelWidgetClass,
2278                      formWidget, timerArgs, XtNumber(timerArgs));
2279 #if ENABLE_NLS
2280     XtSetArg(args[0], XtNfontSet, clockFontSet);
2281 #else
2282     XtSetArg(args[0], XtNfont, clockFontStruct);
2283 #endif
2284     XtSetArg(args[1], XtNtop,    XtChainTop);
2285     XtSetArg(args[2], XtNbottom, XtChainTop);
2286     XtSetValues(whiteTimerWidget, args, 3);
2287
2288     widgetList[j++] = blackTimerWidget =
2289       XtCreateWidget("blackTime", labelWidgetClass,
2290                      formWidget, timerArgs, XtNumber(timerArgs));
2291 #if ENABLE_NLS
2292     XtSetArg(args[0], XtNfontSet, clockFontSet);
2293 #else
2294     XtSetArg(args[0], XtNfont, clockFontStruct);
2295 #endif
2296     XtSetArg(args[1], XtNtop,    XtChainTop);
2297     XtSetArg(args[2], XtNbottom, XtChainTop);
2298     XtSetValues(blackTimerWidget, args, 3);
2299
2300     if (appData.titleInWindow) {
2301         widgetList[j++] = titleWidget =
2302           XtCreateWidget("title", labelWidgetClass, formWidget,
2303                          titleArgs, XtNumber(titleArgs));
2304         XtSetArg(args[0], XtNtop,    XtChainTop);
2305         XtSetArg(args[1], XtNbottom, XtChainTop);
2306         XtSetValues(titleWidget, args, 2);
2307     }
2308
2309     if (appData.showButtonBar) {
2310       widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2311       XtSetArg(args[0], XtNleft,  XtChainRight); // [HGM] glue to right window edge
2312       XtSetArg(args[1], XtNright, XtChainRight); //       for good run-time sizing
2313       XtSetArg(args[2], XtNtop,    XtChainTop);
2314       XtSetArg(args[3], XtNbottom, XtChainTop);
2315       XtSetValues(buttonBarWidget, args, 4);
2316     }
2317
2318     widgetList[j++] = messageWidget =
2319       XtCreateWidget("message", labelWidgetClass, formWidget,
2320                      messageArgs, XtNumber(messageArgs));
2321     XtSetArg(args[0], XtNtop,    XtChainTop);
2322     XtSetArg(args[1], XtNbottom, XtChainTop);
2323     XtSetValues(messageWidget, args, 2);
2324
2325     widgetList[j++] = boardWidget =
2326       XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2327                      XtNumber(boardArgs));
2328
2329     XtManageChildren(widgetList, j);
2330
2331     timerWidth = (boardWidth - sep) / 2;
2332     XtSetArg(args[0], XtNwidth, timerWidth);
2333     XtSetValues(whiteTimerWidget, args, 1);
2334     XtSetValues(blackTimerWidget, args, 1);
2335
2336     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2337     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2338     XtGetValues(whiteTimerWidget, args, 2);
2339
2340     if (appData.showButtonBar) {
2341       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2342       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2343       XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2344     }
2345
2346     /*
2347      * formWidget uses these constraints but they are stored
2348      * in the children.
2349      */
2350     i = 0;
2351     XtSetArg(args[i], XtNfromHoriz, 0); i++;
2352     XtSetValues(menuBarWidget, args, i);
2353     if (appData.titleInWindow) {
2354         if (smallLayout) {
2355             i = 0;
2356             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2357             XtSetValues(whiteTimerWidget, args, i);
2358             i = 0;
2359             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2360             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2361             XtSetValues(blackTimerWidget, args, i);
2362             i = 0;
2363             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2364             XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2365             XtSetValues(titleWidget, args, i);
2366             i = 0;
2367             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2368             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2369             XtSetValues(messageWidget, args, i);
2370             if (appData.showButtonBar) {
2371               i = 0;
2372               XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2373               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2374               XtSetValues(buttonBarWidget, args, i);
2375             }
2376         } else {
2377             i = 0;
2378             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2379             XtSetValues(whiteTimerWidget, args, i);
2380             i = 0;
2381             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2382             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2383             XtSetValues(blackTimerWidget, args, i);
2384             i = 0;
2385             XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2386             XtSetValues(titleWidget, args, i);
2387             i = 0;
2388             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2389             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2390             XtSetValues(messageWidget, args, i);
2391             if (appData.showButtonBar) {
2392               i = 0;
2393               XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2394               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2395               XtSetValues(buttonBarWidget, args, i);
2396             }
2397         }
2398     } else {
2399         i = 0;
2400         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2401         XtSetValues(whiteTimerWidget, args, i);
2402         i = 0;
2403         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2404         XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2405         XtSetValues(blackTimerWidget, args, i);
2406         i = 0;
2407         XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2408         XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2409         XtSetValues(messageWidget, args, i);
2410         if (appData.showButtonBar) {
2411           i = 0;
2412           XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2413           XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2414           XtSetValues(buttonBarWidget, args, i);
2415         }
2416     }
2417     i = 0;
2418     XtSetArg(args[0], XtNfromVert, messageWidget);
2419     XtSetArg(args[1], XtNtop,    XtChainTop);
2420     XtSetArg(args[2], XtNbottom, XtChainBottom);
2421     XtSetArg(args[3], XtNleft,   XtChainLeft);
2422     XtSetArg(args[4], XtNright,  XtChainRight);
2423     XtSetValues(boardWidget, args, 5);
2424
2425     XtRealizeWidget(shellWidget);
2426
2427     if(wpMain.x > 0) {
2428       XtSetArg(args[0], XtNx, wpMain.x);
2429       XtSetArg(args[1], XtNy, wpMain.y);
2430       XtSetValues(shellWidget, args, 2);
2431     }
2432
2433     /*
2434      * Correct the width of the message and title widgets.
2435      * It is not known why some systems need the extra fudge term.
2436      * The value "2" is probably larger than needed.
2437      */
2438     XawFormDoLayout(formWidget, False);
2439
2440 #define WIDTH_FUDGE 2
2441     i = 0;
2442     XtSetArg(args[i], XtNborderWidth, &bor);  i++;
2443     XtSetArg(args[i], XtNheight, &h);  i++;
2444     XtGetValues(messageWidget, args, i);
2445     if (appData.showButtonBar) {
2446       i = 0;
2447       XtSetArg(args[i], XtNwidth, &w);  i++;
2448       XtGetValues(buttonBarWidget, args, i);
2449       w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2450     } else {
2451       w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2452     }
2453
2454     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2455     if (gres != XtGeometryYes && appData.debugMode) {
2456       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2457               programName, gres, w, h, wr, hr);
2458     }
2459
2460     /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2461     /* The size used for the child widget in layout lags one resize behind
2462        its true size, so we resize a second time, 1 pixel smaller.  Yeech! */
2463     w--;
2464     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2465     if (gres != XtGeometryYes && appData.debugMode) {
2466       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2467               programName, gres, w, h, wr, hr);
2468     }
2469     /* !! end hack */
2470     if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
2471     XtSetArg(args[0], XtNleft,  XtChainLeft);  // [HGM] glue ends for good run-time sizing
2472     XtSetArg(args[1], XtNright, XtChainRight);
2473     XtSetValues(messageWidget, args, 2);
2474
2475     if (appData.titleInWindow) {
2476         i = 0;
2477         XtSetArg(args[i], XtNborderWidth, &bor); i++;
2478         XtSetArg(args[i], XtNheight, &h);  i++;
2479         XtGetValues(titleWidget, args, i);
2480         if (smallLayout) {
2481             w = boardWidth - 2*bor;
2482         } else {
2483             XtSetArg(args[0], XtNwidth, &w);
2484             XtGetValues(menuBarWidget, args, 1);
2485             w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2486         }
2487
2488         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2489         if (gres != XtGeometryYes && appData.debugMode) {
2490             fprintf(stderr,
2491                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2492                     programName, gres, w, h, wr, hr);
2493         }
2494     }
2495     XawFormDoLayout(formWidget, True);
2496
2497     xBoardWindow = XtWindow(boardWidget);
2498
2499     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2500     //       not need to go into InitDrawingSizes().
2501 #endif
2502
2503     /*
2504      * Create X checkmark bitmap and initialize option menu checks.
2505      */
2506     ReadBitmap(&xMarkPixmap, "checkmark.bm",
2507                checkmark_bits, checkmark_width, checkmark_height);
2508     XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2509 #ifndef OPTIONSDIALOG
2510     if (appData.alwaysPromoteToQueen) {
2511         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2512                     args, 1);
2513     }
2514     if (appData.animateDragging) {
2515         XtSetValues(XtNameToWidget(menuBarWidget,
2516                                    "menuOptions.Animate Dragging"),
2517                     args, 1);
2518     }
2519     if (appData.animate) {
2520         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2521                     args, 1);
2522     }
2523     if (appData.autoCallFlag) {
2524         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2525                     args, 1);
2526     }
2527     if (appData.autoFlipView) {
2528         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2529                     args, 1);
2530     }
2531     if (appData.blindfold) {
2532         XtSetValues(XtNameToWidget(menuBarWidget,
2533                                    "menuOptions.Blindfold"), args, 1);
2534     }
2535     if (appData.flashCount > 0) {
2536         XtSetValues(XtNameToWidget(menuBarWidget,
2537                                    "menuOptions.Flash Moves"),
2538                     args, 1);
2539     }
2540 #if HIGHDRAG
2541     if (appData.highlightDragging) {
2542         XtSetValues(XtNameToWidget(menuBarWidget,
2543                                    "menuOptions.Highlight Dragging"),
2544                     args, 1);
2545     }
2546 #endif
2547     if (appData.highlightLastMove) {
2548         XtSetValues(XtNameToWidget(menuBarWidget,
2549                                    "menuOptions.Highlight Last Move"),
2550                     args, 1);
2551     }
2552     if (appData.highlightMoveWithArrow) {
2553         XtSetValues(XtNameToWidget(menuBarWidget,
2554                                    "menuOptions.Arrow"),
2555                     args, 1);
2556     }
2557 //    if (appData.icsAlarm) {
2558 //      XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2559 //                  args, 1);
2560 //    }
2561     if (appData.ringBellAfterMoves) {
2562         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2563                     args, 1);
2564     }
2565     if (appData.oneClick) {
2566         XtSetValues(XtNameToWidget(menuBarWidget,
2567                                    "menuOptions.OneClick"), args, 1);
2568     }
2569     if (appData.periodicUpdates) {
2570         XtSetValues(XtNameToWidget(menuBarWidget,
2571                                    "menuOptions.Periodic Updates"), args, 1);
2572     }
2573     if (appData.ponderNextMove) {
2574         XtSetValues(XtNameToWidget(menuBarWidget,
2575                                    "menuOptions.Ponder Next Move"), args, 1);
2576     }
2577     if (appData.popupExitMessage) {
2578         XtSetValues(XtNameToWidget(menuBarWidget,
2579                                    "menuOptions.Popup Exit Message"), args, 1);
2580     }
2581     if (appData.popupMoveErrors) {
2582         XtSetValues(XtNameToWidget(menuBarWidget,
2583                                    "menuOptions.Popup Move Errors"), args, 1);
2584     }
2585 //    if (appData.premove) {
2586 //      XtSetValues(XtNameToWidget(menuBarWidget,
2587 //                                 "menuOptions.Premove"), args, 1);
2588 //    }
2589     if (appData.showCoords) {
2590         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2591                     args, 1);
2592     }
2593     if (appData.hideThinkingFromHuman) {
2594         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2595                     args, 1);
2596     }
2597     if (appData.testLegality) {
2598         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2599                     args, 1);
2600     }
2601 #endif
2602     if (saveSettingsOnExit) {
2603         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2604                     args, 1);
2605     }
2606
2607     /*
2608      * Create an icon.
2609      */
2610     ReadBitmap(&wIconPixmap, "icon_white.bm",
2611                icon_white_bits, icon_white_width, icon_white_height);
2612     ReadBitmap(&bIconPixmap, "icon_black.bm",
2613                icon_black_bits, icon_black_width, icon_black_height);
2614     iconPixmap = wIconPixmap;
2615     i = 0;
2616     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
2617     XtSetValues(shellWidget, args, i);
2618
2619     /*
2620      * Create a cursor for the board widget.
2621      */
2622     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2623     XChangeWindowAttributes(xDisplay, xBoardWindow,
2624                             CWCursor, &window_attributes);
2625
2626     /*
2627      * Inhibit shell resizing.
2628      */
2629     shellArgs[0].value = (XtArgVal) &w;
2630     shellArgs[1].value = (XtArgVal) &h;
2631     XtGetValues(shellWidget, shellArgs, 2);
2632     shellArgs[4].value = shellArgs[2].value = w;
2633     shellArgs[5].value = shellArgs[3].value = h;
2634     XtSetValues(shellWidget, &shellArgs[2], 4);
2635     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2636     marginH =  h - boardHeight;
2637
2638     CatchDeleteWindow(shellWidget, "QuitProc");
2639
2640     CreateGCs(False);
2641     CreateGrid();
2642     CreateAnyPieces();
2643
2644     CreatePieceMenus();
2645
2646     if (appData.animate || appData.animateDragging)
2647       CreateAnimVars();
2648
2649     XtAugmentTranslations(formWidget,
2650                           XtParseTranslationTable(globalTranslations));
2651     XtAugmentTranslations(boardWidget,
2652                           XtParseTranslationTable(boardTranslations));
2653     XtAugmentTranslations(whiteTimerWidget,
2654                           XtParseTranslationTable(whiteTranslations));
2655     XtAugmentTranslations(blackTimerWidget,
2656                           XtParseTranslationTable(blackTranslations));
2657
2658     /* Why is the following needed on some versions of X instead
2659      * of a translation? */
2660     XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2661                       (XtEventHandler) EventProc, NULL);
2662     /* end why */
2663     XtAddEventHandler(formWidget, KeyPressMask, False,
2664                       (XtEventHandler) MoveTypeInProc, NULL);
2665
2666     /* [AS] Restore layout */
2667     if( wpMoveHistory.visible ) {
2668       HistoryPopUp();
2669     }
2670
2671     if( wpEvalGraph.visible )
2672       {
2673         EvalGraphPopUp();
2674       };
2675
2676     if( wpEngineOutput.visible ) {
2677       EngineOutputPopUp();
2678     }
2679
2680     InitBackEnd2();
2681
2682     if (errorExitStatus == -1) {
2683         if (appData.icsActive) {
2684             /* We now wait until we see "login:" from the ICS before
2685                sending the logon script (problems with timestamp otherwise) */
2686             /*ICSInitScript();*/
2687             if (appData.icsInputBox) ICSInputBoxPopUp();
2688         }
2689
2690     #ifdef SIGWINCH
2691     signal(SIGWINCH, TermSizeSigHandler);
2692     #endif
2693         signal(SIGINT, IntSigHandler);
2694         signal(SIGTERM, IntSigHandler);
2695         if (*appData.cmailGameName != NULLCHAR) {
2696             signal(SIGUSR1, CmailSigHandler);
2697         }
2698     }
2699     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2700     InitPosition(TRUE);
2701 //    XtSetKeyboardFocus(shellWidget, formWidget);
2702     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2703
2704     XtAppMainLoop(appContext);
2705     if (appData.debugMode) fclose(debugFP); // [DM] debug
2706     return 0;
2707 }
2708
2709 static Boolean noEcho;
2710
2711 void
2712 ShutDownFrontEnd ()
2713 {
2714     if (appData.icsActive && oldICSInteractionTitle != NULL) {
2715         DisplayIcsInteractionTitle(oldICSInteractionTitle);
2716     }
2717     if (saveSettingsOnExit) SaveSettings(settingsFileName);
2718     unlink(gameCopyFilename);
2719     unlink(gamePasteFilename);
2720     if(noEcho) EchoOn();
2721 }
2722
2723 RETSIGTYPE
2724 TermSizeSigHandler (int sig)
2725 {
2726     update_ics_width();
2727 }
2728
2729 RETSIGTYPE
2730 IntSigHandler (int sig)
2731 {
2732     ExitEvent(sig);
2733 }
2734
2735 RETSIGTYPE
2736 CmailSigHandler (int sig)
2737 {
2738     int dummy = 0;
2739     int error;
2740
2741     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
2742
2743     /* Activate call-back function CmailSigHandlerCallBack()             */
2744     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2745
2746     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2747 }
2748
2749 void
2750 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2751 {
2752     BoardToTop();
2753     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
2754 }
2755 /**** end signal code ****/
2756
2757
2758 void
2759 ICSInitScript ()
2760 {
2761   /* try to open the icsLogon script, either in the location given
2762    * or in the users HOME directory
2763    */
2764
2765   FILE *f;
2766   char buf[MSG_SIZ];
2767   char *homedir;
2768
2769   f = fopen(appData.icsLogon, "r");
2770   if (f == NULL)
2771     {
2772       homedir = getenv("HOME");
2773       if (homedir != NULL)
2774         {
2775           safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2776           strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2777           strncat(buf, appData.icsLogon,  MSG_SIZ - strlen(buf) - 1);
2778           f = fopen(buf, "r");
2779         }
2780     }
2781
2782   if (f != NULL)
2783     ProcessICSInitScript(f);
2784   else
2785     printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2786
2787   return;
2788 }
2789
2790 void
2791 ResetFrontEnd ()
2792 {
2793     CommentPopDown();
2794     TagsPopDown();
2795     return;
2796 }
2797
2798 typedef struct {
2799     char *name;
2800     Boolean value;
2801 } Enables;
2802
2803 void
2804 GreyRevert (Boolean grey)
2805 {
2806     Widget w;
2807     if (!menuBarWidget) return;
2808     w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2809     if (w == NULL) {
2810       DisplayError("menuEdit.Revert", 0);
2811     } else {
2812       XtSetSensitive(w, !grey);
2813     }
2814     w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2815     if (w == NULL) {
2816       DisplayError("menuEdit.Annotate", 0);
2817     } else {
2818       XtSetSensitive(w, !grey);
2819     }
2820 }
2821
2822 void
2823 SetMenuEnables (Enables *enab)
2824 {
2825   Widget w;
2826   if (!menuBarWidget) return;
2827   while (enab->name != NULL) {
2828     w = XtNameToWidget(menuBarWidget, enab->name);
2829     if (w == NULL) {
2830       DisplayError(enab->name, 0);
2831     } else {
2832       XtSetSensitive(w, enab->value);
2833     }
2834     enab++;
2835   }
2836 }
2837
2838 Enables icsEnables[] = {
2839     { "menuFile.Mail Move", False },
2840     { "menuFile.Reload CMail Message", False },
2841     { "menuMode.Machine Black", False },
2842     { "menuMode.Machine White", False },
2843     { "menuMode.Analysis Mode", False },
2844     { "menuMode.Analyze File", False },
2845     { "menuMode.Two Machines", False },
2846     { "menuMode.Machine Match", False },
2847 #ifndef ZIPPY
2848     { "menuEngine.Hint", False },
2849     { "menuEngine.Book", False },
2850     { "menuEngine.Move Now", False },
2851 #ifndef OPTIONSDIALOG
2852     { "menuOptions.Periodic Updates", False },
2853     { "menuOptions.Hide Thinking", False },
2854     { "menuOptions.Ponder Next Move", False },
2855 #endif
2856 #endif
2857     { "menuEngine.Engine #1 Settings", False },
2858     { "menuEngine.Engine #2 Settings", False },
2859     { "menuEngine.Load Engine", False },
2860     { "menuEdit.Annotate", False },
2861     { "menuOptions.Match", False },
2862     { NULL, False }
2863 };
2864
2865 Enables ncpEnables[] = {
2866     { "menuFile.Mail Move", False },
2867     { "menuFile.Reload CMail Message", False },
2868     { "menuMode.Machine White", False },
2869     { "menuMode.Machine Black", False },
2870     { "menuMode.Analysis Mode", False },
2871     { "menuMode.Analyze File", False },
2872     { "menuMode.Two Machines", False },
2873     { "menuMode.Machine Match", False },
2874     { "menuMode.ICS Client", False },
2875     { "menuView.ICStex", False },
2876     { "menuView.ICS Input Box", False },
2877     { "Action", False },
2878     { "menuEdit.Revert", False },
2879     { "menuEdit.Annotate", False },
2880     { "menuEngine.Engine #1 Settings", False },
2881     { "menuEngine.Engine #2 Settings", False },
2882     { "menuEngine.Move Now", False },
2883     { "menuEngine.Retract Move", False },
2884     { "menuOptions.ICS", False },
2885 #ifndef OPTIONSDIALOG
2886     { "menuOptions.Auto Flag", False },
2887     { "menuOptions.Auto Flip View", False },
2888 //    { "menuOptions.ICS Alarm", False },
2889     { "menuOptions.Move Sound", False },
2890     { "menuOptions.Hide Thinking", False },
2891     { "menuOptions.Periodic Updates", False },
2892     { "menuOptions.Ponder Next Move", False },
2893 #endif
2894     { "menuEngine.Hint", False },
2895     { "menuEngine.Book", False },
2896     { NULL, False }
2897 };
2898
2899 Enables gnuEnables[] = {
2900     { "menuMode.ICS Client", False },
2901     { "menuView.ICStex", False },
2902     { "menuView.ICS Input Box", False },
2903     { "menuAction.Accept", False },
2904     { "menuAction.Decline", False },
2905     { "menuAction.Rematch", False },
2906     { "menuAction.Adjourn", False },
2907     { "menuAction.Stop Examining", False },
2908     { "menuAction.Stop Observing", False },
2909     { "menuAction.Upload to Examine", False },
2910     { "menuEdit.Revert", False },
2911     { "menuEdit.Annotate", False },
2912     { "menuOptions.ICS", False },
2913
2914     /* The next two options rely on SetCmailMode being called *after*    */
2915     /* SetGNUMode so that when GNU is being used to give hints these     */
2916     /* menu options are still available                                  */
2917
2918     { "menuFile.Mail Move", False },
2919     { "menuFile.Reload CMail Message", False },
2920     // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2921     { "menuMode.Machine White", True },
2922     { "menuMode.Machine Black", True },
2923     { "menuMode.Analysis Mode", True },
2924     { "menuMode.Analyze File", True },
2925     { "menuMode.Two Machines", True },
2926     { "menuMode.Machine Match", True },
2927     { "menuEngine.Engine #1 Settings", True },
2928     { "menuEngine.Engine #2 Settings", True },
2929     { "menuEngine.Hint", True },
2930     { "menuEngine.Book", True },
2931     { "menuEngine.Move Now", True },
2932     { "menuEngine.Retract Move", True },
2933     { "Action", True },
2934     { NULL, False }
2935 };
2936
2937 Enables cmailEnables[] = {
2938     { "Action", True },
2939     { "menuAction.Call Flag", False },
2940     { "menuAction.Draw", True },
2941     { "menuAction.Adjourn", False },
2942     { "menuAction.Abort", False },
2943     { "menuAction.Stop Observing", False },
2944     { "menuAction.Stop Examining", False },
2945     { "menuFile.Mail Move", True },
2946     { "menuFile.Reload CMail Message", True },
2947     { NULL, False }
2948 };
2949
2950 Enables trainingOnEnables[] = {
2951   { "menuMode.Edit Comment", False },
2952   { "menuMode.Pause", False },
2953   { "menuEdit.Forward", False },
2954   { "menuEdit.Backward", False },
2955   { "menuEdit.Forward to End", False },
2956   { "menuEdit.Back to Start", False },
2957   { "menuEngine.Move Now", False },
2958   { "menuEdit.Truncate Game", False },
2959   { NULL, False }
2960 };
2961
2962 Enables trainingOffEnables[] = {
2963   { "menuMode.Edit Comment", True },
2964   { "menuMode.Pause", True },
2965   { "menuEdit.Forward", True },
2966   { "menuEdit.Backward", True },
2967   { "menuEdit.Forward to End", True },
2968   { "menuEdit.Back to Start", True },
2969   { "menuEngine.Move Now", True },
2970   { "menuEdit.Truncate Game", True },
2971   { NULL, False }
2972 };
2973
2974 Enables machineThinkingEnables[] = {
2975   { "menuFile.Load Game", False },
2976 //  { "menuFile.Load Next Game", False },
2977 //  { "menuFile.Load Previous Game", False },
2978 //  { "menuFile.Reload Same Game", False },
2979   { "menuEdit.Paste Game", False },
2980   { "menuFile.Load Position", False },
2981 //  { "menuFile.Load Next Position", False },
2982 //  { "menuFile.Load Previous Position", False },
2983 //  { "menuFile.Reload Same Position", False },
2984   { "menuEdit.Paste Position", False },
2985   { "menuMode.Machine White", False },
2986   { "menuMode.Machine Black", False },
2987   { "menuMode.Two Machines", False },
2988 //  { "menuMode.Machine Match", False },
2989   { "menuEngine.Retract Move", False },
2990   { NULL, False }
2991 };
2992
2993 Enables userThinkingEnables[] = {
2994   { "menuFile.Load Game", True },
2995 //  { "menuFile.Load Next Game", True },
2996 //  { "menuFile.Load Previous Game", True },
2997 //  { "menuFile.Reload Same Game", True },
2998   { "menuEdit.Paste Game", True },
2999   { "menuFile.Load Position", True },
3000 //  { "menuFile.Load Next Position", True },
3001 //  { "menuFile.Load Previous Position", True },
3002 //  { "menuFile.Reload Same Position", True },
3003   { "menuEdit.Paste Position", True },
3004   { "menuMode.Machine White", True },
3005   { "menuMode.Machine Black", True },
3006   { "menuMode.Two Machines", True },
3007 //  { "menuMode.Machine Match", True },
3008   { "menuEngine.Retract Move", True },
3009   { NULL, False }
3010 };
3011
3012 void
3013 SetICSMode ()
3014 {
3015   SetMenuEnables(icsEnables);
3016
3017 #if ZIPPY
3018   if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3019      XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3020      XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3021   }
3022 #endif
3023 }
3024
3025 void
3026 SetNCPMode ()
3027 {
3028   SetMenuEnables(ncpEnables);
3029 }
3030
3031 void
3032 SetGNUMode ()
3033 {
3034   SetMenuEnables(gnuEnables);
3035 }
3036
3037 void
3038 SetCmailMode ()
3039 {
3040   SetMenuEnables(cmailEnables);
3041 }
3042
3043 void
3044 SetTrainingModeOn ()
3045 {
3046   SetMenuEnables(trainingOnEnables);
3047   if (appData.showButtonBar) {
3048     XtSetSensitive(buttonBarWidget, False);
3049   }
3050   CommentPopDown();
3051 }
3052
3053 void
3054 SetTrainingModeOff ()
3055 {
3056   SetMenuEnables(trainingOffEnables);
3057   if (appData.showButtonBar) {
3058     XtSetSensitive(buttonBarWidget, True);
3059   }
3060 }
3061
3062 void
3063 SetUserThinkingEnables ()
3064 {
3065   if (appData.noChessProgram) return;
3066   SetMenuEnables(userThinkingEnables);
3067 }
3068
3069 void
3070 SetMachineThinkingEnables ()
3071 {
3072   if (appData.noChessProgram) return;
3073   SetMenuEnables(machineThinkingEnables);
3074   switch (gameMode) {
3075   case MachinePlaysBlack:
3076   case MachinePlaysWhite:
3077   case TwoMachinesPlay:
3078     XtSetSensitive(XtNameToWidget(menuBarWidget,
3079                                   ModeToWidgetName(gameMode)), True);
3080     break;
3081   default:
3082     break;
3083   }
3084 }
3085
3086 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3087 #define HISTORY_SIZE 64
3088 static char *history[HISTORY_SIZE];
3089 int histIn = 0, histP = 0;
3090
3091 void
3092 SaveInHistory (char *cmd)
3093 {
3094   if (history[histIn] != NULL) {
3095     free(history[histIn]);
3096     history[histIn] = NULL;
3097   }
3098   if (*cmd == NULLCHAR) return;
3099   history[histIn] = StrSave(cmd);
3100   histIn = (histIn + 1) % HISTORY_SIZE;
3101   if (history[histIn] != NULL) {
3102     free(history[histIn]);
3103     history[histIn] = NULL;
3104   }
3105   histP = histIn;
3106 }
3107
3108 char *
3109 PrevInHistory (char *cmd)
3110 {
3111   int newhp;
3112   if (histP == histIn) {
3113     if (history[histIn] != NULL) free(history[histIn]);
3114     history[histIn] = StrSave(cmd);
3115   }
3116   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3117   if (newhp == histIn || history[newhp] == NULL) return NULL;
3118   histP = newhp;
3119   return history[histP];
3120 }
3121
3122 char *
3123 NextInHistory ()
3124 {
3125   if (histP == histIn) return NULL;
3126   histP = (histP + 1) % HISTORY_SIZE;
3127   return history[histP];   
3128 }
3129 // end of borrowed code
3130
3131 #define Abs(n) ((n)<0 ? -(n) : (n))
3132
3133 #ifdef ENABLE_NLS
3134 char *
3135 InsertPxlSize (char *pattern, int targetPxlSize)
3136 {
3137     char *base_fnt_lst, strInt[12], *p, *q;
3138     int alternatives, i, len, strIntLen;
3139
3140     /*
3141      * Replace the "*" (if present) in the pixel-size slot of each
3142      * alternative with the targetPxlSize.
3143      */
3144     p = pattern;
3145     alternatives = 1;
3146     while ((p = strchr(p, ',')) != NULL) {
3147       alternatives++;
3148       p++;
3149     }
3150     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3151     strIntLen = strlen(strInt);
3152     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3153
3154     p = pattern;
3155     q = base_fnt_lst;
3156     while (alternatives--) {
3157       char *comma = strchr(p, ',');
3158       for (i=0; i<14; i++) {
3159         char *hyphen = strchr(p, '-');
3160         if (!hyphen) break;
3161         if (comma && hyphen > comma) break;
3162         len = hyphen + 1 - p;
3163         if (i == 7 && *p == '*' && len == 2) {
3164           p += len;
3165           memcpy(q, strInt, strIntLen);
3166           q += strIntLen;
3167           *q++ = '-';
3168         } else {
3169           memcpy(q, p, len);
3170           p += len;
3171           q += len;
3172         }
3173       }
3174       if (!comma) break;
3175       len = comma + 1 - p;
3176       memcpy(q, p, len);
3177       p += len;
3178       q += len;
3179     }
3180     strcpy(q, p);
3181
3182     return base_fnt_lst;
3183 }
3184
3185 XFontSet
3186 CreateFontSet (char *base_fnt_lst)
3187 {
3188     XFontSet fntSet;
3189     char **missing_list;
3190     int missing_count;
3191     char *def_string;
3192
3193     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3194                             &missing_list, &missing_count, &def_string);
3195     if (appData.debugMode) {
3196       int i, count;
3197       XFontStruct **font_struct_list;
3198       char **font_name_list;
3199       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3200       if (fntSet) {
3201         fprintf(debugFP, " got list %s, locale %s\n",
3202                 XBaseFontNameListOfFontSet(fntSet),
3203                 XLocaleOfFontSet(fntSet));
3204         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3205         for (i = 0; i < count; i++) {
3206           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3207         }
3208       }
3209       for (i = 0; i < missing_count; i++) {
3210         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3211       }
3212     }
3213     if (fntSet == NULL) {
3214       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3215       exit(2);
3216     }
3217     return fntSet;
3218 }
3219 #else // not ENABLE_NLS
3220 /*
3221  * Find a font that matches "pattern" that is as close as
3222  * possible to the targetPxlSize.  Prefer fonts that are k
3223  * pixels smaller to fonts that are k pixels larger.  The
3224  * pattern must be in the X Consortium standard format,
3225  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3226  * The return value should be freed with XtFree when no
3227  * longer needed.
3228  */
3229 char *
3230 FindFont (char *pattern, int targetPxlSize)
3231 {
3232     char **fonts, *p, *best, *scalable, *scalableTail;
3233     int i, j, nfonts, minerr, err, pxlSize;
3234
3235     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3236     if (nfonts < 1) {
3237         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3238                 programName, pattern);
3239         exit(2);
3240     }
3241
3242     best = fonts[0];
3243     scalable = NULL;
3244     minerr = 999999;
3245     for (i=0; i<nfonts; i++) {
3246         j = 0;
3247         p = fonts[i];
3248         if (*p != '-') continue;
3249         while (j < 7) {
3250             if (*p == NULLCHAR) break;
3251             if (*p++ == '-') j++;
3252         }
3253         if (j < 7) continue;
3254         pxlSize = atoi(p);
3255         if (pxlSize == 0) {
3256             scalable = fonts[i];
3257             scalableTail = p;
3258         } else {
3259             err = pxlSize - targetPxlSize;
3260             if (Abs(err) < Abs(minerr) ||
3261                 (minerr > 0 && err < 0 && -err == minerr)) {
3262                 best = fonts[i];
3263                 minerr = err;
3264             }
3265         }
3266     }
3267     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3268         /* If the error is too big and there is a scalable font,
3269            use the scalable font. */
3270         int headlen = scalableTail - scalable;
3271         p = (char *) XtMalloc(strlen(scalable) + 10);
3272         while (isdigit(*scalableTail)) scalableTail++;
3273         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3274     } else {
3275         p = (char *) XtMalloc(strlen(best) + 2);
3276         safeStrCpy(p, best, strlen(best)+1 );
3277     }
3278     if (appData.debugMode) {
3279         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
3280                 pattern, targetPxlSize, p);
3281     }
3282     XFreeFontNames(fonts);
3283     return p;
3284 }
3285 #endif
3286
3287 void
3288 DeleteGCs ()
3289 {   // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3290     // must be called before all non-first callse to CreateGCs()
3291     XtReleaseGC(shellWidget, highlineGC);
3292     XtReleaseGC(shellWidget, lightSquareGC);
3293     XtReleaseGC(shellWidget, darkSquareGC);
3294     XtReleaseGC(shellWidget, lineGC);
3295     if (appData.monoMode) {
3296         if (DefaultDepth(xDisplay, xScreen) == 1) {
3297             XtReleaseGC(shellWidget, wbPieceGC);
3298         } else {
3299             XtReleaseGC(shellWidget, bwPieceGC);
3300         }
3301     } else {
3302         XtReleaseGC(shellWidget, prelineGC);
3303         XtReleaseGC(shellWidget, jailSquareGC);
3304         XtReleaseGC(shellWidget, wdPieceGC);
3305         XtReleaseGC(shellWidget, wlPieceGC);
3306         XtReleaseGC(shellWidget, wjPieceGC);
3307         XtReleaseGC(shellWidget, bdPieceGC);
3308         XtReleaseGC(shellWidget, blPieceGC);
3309         XtReleaseGC(shellWidget, bjPieceGC);
3310     }
3311 }
3312
3313 void
3314 CreateGCs (int redo)
3315 {
3316     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3317       | GCBackground | GCFunction | GCPlaneMask;
3318     XGCValues gc_values;
3319     GC copyInvertedGC;
3320
3321     gc_values.plane_mask = AllPlanes;
3322     gc_values.line_width = lineGap;
3323     gc_values.line_style = LineSolid;
3324     gc_values.function = GXcopy;
3325
3326   if(redo) {
3327     DeleteGCs(); // called a second time; clean up old GCs first
3328   } else { // [HGM] grid and font GCs created on first call only
3329     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3330     gc_values.background = XWhitePixel(xDisplay, xScreen);
3331     coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3332     XSetFont(xDisplay, coordGC, coordFontID);
3333
3334     // [HGM] make font for holdings counts (white on black)
3335     gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3336     gc_values.background = XBlackPixel(xDisplay, xScreen);
3337     countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3338     XSetFont(xDisplay, countGC, countFontID);
3339   }
3340     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3341     gc_values.background = XBlackPixel(xDisplay, xScreen);
3342     lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3343
3344     if (appData.monoMode) {
3345         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3346         gc_values.background = XWhitePixel(xDisplay, xScreen);
3347         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3348
3349         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3350         gc_values.background = XBlackPixel(xDisplay, xScreen);
3351         lightSquareGC = wbPieceGC
3352           = XtGetGC(shellWidget, value_mask, &gc_values);
3353
3354         gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3355         gc_values.background = XWhitePixel(xDisplay, xScreen);
3356         darkSquareGC = bwPieceGC
3357           = XtGetGC(shellWidget, value_mask, &gc_values);
3358
3359         if (DefaultDepth(xDisplay, xScreen) == 1) {
3360             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3361             gc_values.function = GXcopyInverted;
3362             copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3363             gc_values.function = GXcopy;
3364             if (XBlackPixel(xDisplay, xScreen) == 1) {
3365                 bwPieceGC = darkSquareGC;
3366                 wbPieceGC = copyInvertedGC;
3367             } else {
3368                 bwPieceGC = copyInvertedGC;
3369                 wbPieceGC = lightSquareGC;
3370             }
3371         }
3372     } else {
3373         gc_values.foreground = highlightSquareColor;
3374         gc_values.background = highlightSquareColor;
3375         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3376
3377         gc_values.foreground = premoveHighlightColor;
3378         gc_values.background = premoveHighlightColor;
3379         prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3380
3381         gc_values.foreground = lightSquareColor;
3382         gc_values.background = darkSquareColor;
3383         lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3384
3385         gc_values.foreground = darkSquareColor;
3386         gc_values.background = lightSquareColor;
3387         darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3388
3389         gc_values.foreground = jailSquareColor;
3390         gc_values.background = jailSquareColor;
3391         jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3392
3393         gc_values.foreground = whitePieceColor;
3394         gc_values.background = darkSquareColor;
3395         wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3396
3397         gc_values.foreground = whitePieceColor;
3398         gc_values.background = lightSquareColor;
3399         wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3400
3401         gc_values.foreground = whitePieceColor;
3402         gc_values.background = jailSquareColor;
3403         wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3404
3405         gc_values.foreground = blackPieceColor;
3406         gc_values.background = darkSquareColor;
3407         bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3408
3409         gc_values.foreground = blackPieceColor;
3410         gc_values.background = lightSquareColor;
3411         blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3412
3413         gc_values.foreground = blackPieceColor;
3414         gc_values.background = jailSquareColor;
3415         bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3416     }
3417 }
3418
3419 void
3420 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
3421 {
3422     int x, y, w, h, p;
3423     FILE *fp;
3424     Pixmap temp;
3425     XGCValues   values;
3426     GC maskGC;
3427
3428     fp = fopen(filename, "rb");
3429     if (!fp) {
3430         fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3431         exit(1);
3432     }
3433
3434     w = fgetc(fp);
3435     h = fgetc(fp);
3436
3437     for (y=0; y<h; ++y) {
3438         for (x=0; x<h; ++x) {
3439             p = fgetc(fp);
3440
3441             switch (p) {
3442               case 0:
3443                 XPutPixel(xim, x, y, blackPieceColor);
3444                 if (xmask)
3445                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3446                 break;
3447               case 1:
3448                 XPutPixel(xim, x, y, darkSquareColor);
3449                 if (xmask)
3450                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3451                 break;
3452               case 2:
3453                 XPutPixel(xim, x, y, whitePieceColor);
3454                 if (xmask)
3455                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3456                 break;
3457               case 3:
3458                 XPutPixel(xim, x, y, lightSquareColor);
3459                 if (xmask)
3460                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3461                 break;
3462             }
3463         }
3464     }
3465
3466     fclose(fp);
3467
3468     /* create Pixmap of piece */
3469     *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3470                           w, h, xim->depth);
3471     XPutImage(xDisplay, *dest, lightSquareGC, xim,
3472               0, 0, 0, 0, w, h);
3473
3474     /* create Pixmap of clipmask
3475        Note: We assume the white/black pieces have the same
3476              outline, so we make only 6 masks. This is okay
3477              since the XPM clipmask routines do the same. */
3478     if (xmask) {
3479       temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3480                             w, h, xim->depth);
3481       XPutImage(xDisplay, temp, lightSquareGC, xmask,
3482               0, 0, 0, 0, w, h);
3483
3484       /* now create the 1-bit version */
3485       *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3486                           w, h, 1);
3487
3488       values.foreground = 1;
3489       values.background = 0;
3490
3491       /* Don't use XtGetGC, not read only */
3492       maskGC = XCreateGC(xDisplay, *mask,
3493                     GCForeground | GCBackground, &values);
3494       XCopyPlane(xDisplay, temp, *mask, maskGC,
3495                   0, 0, squareSize, squareSize, 0, 0, 1);
3496       XFreePixmap(xDisplay, temp);
3497     }
3498 }
3499
3500
3501 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3502
3503 void
3504 CreateXIMPieces ()
3505 {
3506     int piece, kind;
3507     char buf[MSG_SIZ];
3508     u_int ss;
3509     static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3510     XImage *ximtemp;
3511
3512     ss = squareSize;
3513
3514     /* The XSynchronize calls were copied from CreatePieces.
3515        Not sure if needed, but can't hurt */
3516     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3517                                      buffering bug */
3518
3519     /* temp needed by loadXIM() */
3520     ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3521                  0, 0, ss, ss, AllPlanes, XYPixmap);
3522
3523     if (strlen(appData.pixmapDirectory) == 0) {
3524       useImages = 0;
3525     } else {
3526         useImages = 1;
3527         if (appData.monoMode) {
3528           DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3529                             0, 2);
3530           ExitEvent(2);
3531         }
3532         fprintf(stderr, _("\nLoading XIMs...\n"));
3533         /* Load pieces */
3534         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3535             fprintf(stderr, "%d", piece+1);
3536             for (kind=0; kind<4; kind++) {
3537                 fprintf(stderr, ".");
3538                 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3539                         ExpandPathName(appData.pixmapDirectory),
3540                         piece <= (int) WhiteKing ? "" : "w",
3541                         pieceBitmapNames[piece],
3542                         ximkind[kind], ss);
3543                 ximPieceBitmap[kind][piece] =
3544                   XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3545                             0, 0, ss, ss, AllPlanes, XYPixmap);
3546                 if (appData.debugMode)
3547                   fprintf(stderr, _("(File:%s:) "), buf);
3548                 loadXIM(ximPieceBitmap[kind][piece],
3549                         ximtemp, buf,
3550                         &(xpmPieceBitmap2[kind][piece]),
3551                         &(ximMaskPm2[piece]));
3552                 if(piece <= (int)WhiteKing)
3553                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3554             }
3555             fprintf(stderr," ");
3556         }
3557         /* Load light and dark squares */
3558         /* If the LSQ and DSQ pieces don't exist, we will
3559            draw them with solid squares. */
3560         snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3561         if (access(buf, 0) != 0) {
3562             useImageSqs = 0;
3563         } else {
3564             useImageSqs = 1;
3565             fprintf(stderr, _("light square "));
3566             ximLightSquare=
3567               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3568                         0, 0, ss, ss, AllPlanes, XYPixmap);
3569             if (appData.debugMode)
3570               fprintf(stderr, _("(File:%s:) "), buf);
3571
3572             loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3573             fprintf(stderr, _("dark square "));
3574             snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3575                     ExpandPathName(appData.pixmapDirectory), ss);
3576             if (appData.debugMode)
3577               fprintf(stderr, _("(File:%s:) "), buf);
3578             ximDarkSquare=
3579               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3580                         0, 0, ss, ss, AllPlanes, XYPixmap);
3581             loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3582             xpmJailSquare = xpmLightSquare;
3583         }
3584         fprintf(stderr, _("Done.\n"));
3585     }
3586     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3587 }
3588
3589 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3590
3591 #if HAVE_LIBXPM
3592 void
3593 CreateXPMBoard (char *s, int kind)
3594 {
3595     XpmAttributes attr;
3596     attr.valuemask = 0;
3597     if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3598     if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3599         useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3600     }
3601 }
3602
3603 void
3604 FreeXPMPieces ()
3605 {   // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3606     // thisroutine has to be called t free the old piece pixmaps
3607     int piece, kind;
3608     for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3609         for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3610     if(useImageSqs) {
3611         XFreePixmap(xDisplay, xpmLightSquare);
3612         XFreePixmap(xDisplay, xpmDarkSquare);
3613     }
3614 }
3615
3616 void
3617 CreateXPMPieces ()
3618 {
3619     int piece, kind, r;
3620     char buf[MSG_SIZ];
3621     u_int ss = squareSize;
3622     XpmAttributes attr;
3623     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3624     XpmColorSymbol symbols[4];
3625     static int redo = False;
3626
3627     if(redo) FreeXPMPieces(); else redo = 1;
3628
3629     /* The XSynchronize calls were copied from CreatePieces.
3630        Not sure if needed, but can't hurt */
3631     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3632
3633     /* Setup translations so piece colors match square colors */
3634     symbols[0].name = "light_piece";
3635     symbols[0].value = appData.whitePieceColor;
3636     symbols[1].name = "dark_piece";
3637     symbols[1].value = appData.blackPieceColor;
3638     symbols[2].name = "light_square";
3639     symbols[2].value = appData.lightSquareColor;
3640     symbols[3].name = "dark_square";
3641     symbols[3].value = appData.darkSquareColor;
3642
3643     attr.valuemask = XpmColorSymbols;
3644     attr.colorsymbols = symbols;
3645     attr.numsymbols = 4;
3646
3647     if (appData.monoMode) {
3648       DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3649                         0, 2);
3650       ExitEvent(2);
3651     }
3652     if (strlen(appData.pixmapDirectory) == 0) {
3653         XpmPieces* pieces = builtInXpms;
3654         useImages = 1;
3655         /* Load pieces */
3656         while (pieces->size != squareSize && pieces->size) pieces++;
3657         if (!pieces->size) {
3658           fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3659           exit(1);
3660         }
3661         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3662             for (kind=0; kind<4; kind++) {
3663
3664                 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3665                                                pieces->xpm[piece][kind],
3666                                                &(xpmPieceBitmap2[kind][piece]),
3667                                                NULL, &attr)) != 0) {
3668                   fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3669                           r, buf);
3670                   exit(1);
3671                 }
3672                 if(piece <= (int) WhiteKing)
3673                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3674             }
3675         }
3676         useImageSqs = 0;
3677         xpmJailSquare = xpmLightSquare;
3678     } else {
3679         useImages = 1;
3680
3681         fprintf(stderr, _("\nLoading XPMs...\n"));
3682
3683         /* Load pieces */
3684         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3685             fprintf(stderr, "%d ", piece+1);
3686             for (kind=0; kind<4; kind++) {
3687               snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3688                         ExpandPathName(appData.pixmapDirectory),
3689                         piece > (int) WhiteKing ? "w" : "",
3690                         pieceBitmapNames[piece],
3691                         xpmkind[kind], ss);
3692                 if (appData.debugMode) {
3693                     fprintf(stderr, _("(File:%s:) "), buf);
3694                 }
3695                 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3696                                            &(xpmPieceBitmap2[kind][piece]),
3697                                            NULL, &attr)) != 0) {
3698                     if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3699                       // [HGM] missing: read of unorthodox piece failed; substitute King.
3700                       snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3701                                 ExpandPathName(appData.pixmapDirectory),
3702                                 xpmkind[kind], ss);
3703                         if (appData.debugMode) {
3704                             fprintf(stderr, _("(Replace by File:%s:) "), buf);
3705                         }
3706                         r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3707                                                 &(xpmPieceBitmap2[kind][piece]),
3708                                                 NULL, &attr);
3709                     }
3710                     if (r != 0) {
3711                         fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3712                                 r, buf);
3713                         exit(1);
3714                     }
3715                 }
3716                 if(piece <= (int) WhiteKing)
3717                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3718             }
3719         }
3720         /* Load light and dark squares */
3721         /* If the LSQ and DSQ pieces don't exist, we will
3722            draw them with solid squares. */
3723         fprintf(stderr, _("light square "));
3724         snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3725         if (access(buf, 0) != 0) {
3726             useImageSqs = 0;
3727         } else {
3728             useImageSqs = 1;
3729             if (appData.debugMode)
3730               fprintf(stderr, _("(File:%s:) "), buf);
3731
3732             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3733                                        &xpmLightSquare, NULL, &attr)) != 0) {
3734                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3735                 exit(1);
3736             }
3737             fprintf(stderr, _("dark square "));
3738             snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3739                     ExpandPathName(appData.pixmapDirectory), ss);
3740             if (appData.debugMode) {
3741                 fprintf(stderr, _("(File:%s:) "), buf);
3742             }
3743             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3744                                        &xpmDarkSquare, NULL, &attr)) != 0) {
3745                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3746                 exit(1);
3747             }
3748         }
3749         xpmJailSquare = xpmLightSquare;
3750         fprintf(stderr, _("Done.\n"));
3751     }
3752     oldVariant = -1; // kludge to force re-makig of animation masks
3753     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3754                                       buffering bug */
3755 }
3756 #endif /* HAVE_LIBXPM */
3757
3758 #if HAVE_LIBXPM
3759 /* No built-in bitmaps */
3760 void CreatePieces()
3761 {
3762     int piece, kind;
3763     char buf[MSG_SIZ];
3764     u_int ss = squareSize;
3765
3766     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3767                                      buffering bug */
3768
3769     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3770         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3771           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3772                    pieceBitmapNames[piece],
3773                    ss, kind == SOLID ? 's' : 'o');
3774           ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3775           if(piece <= (int)WhiteKing)
3776             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3777         }
3778     }
3779
3780     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3781                                       buffering bug */
3782 }
3783 #else
3784 /* With built-in bitmaps */
3785 void
3786 CreatePieces ()
3787 {
3788     BuiltInBits* bib = builtInBits;
3789     int piece, kind;
3790     char buf[MSG_SIZ];
3791     u_int ss = squareSize;
3792
3793     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3794                                      buffering bug */
3795
3796     while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3797
3798     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3799         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3800           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3801                    pieceBitmapNames[piece],
3802                    ss, kind == SOLID ? 's' : 'o');
3803           ReadBitmap(&pieceBitmap2[kind][piece], buf,
3804                      bib->bits[kind][piece], ss, ss);
3805           if(piece <= (int)WhiteKing)
3806             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3807         }
3808     }
3809
3810     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3811                                       buffering bug */
3812 }
3813 #endif
3814
3815 void
3816 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
3817 {
3818     int x_hot, y_hot;
3819     u_int w, h;
3820     int errcode;
3821     char msg[MSG_SIZ], fullname[MSG_SIZ];
3822
3823     if (*appData.bitmapDirectory != NULLCHAR) {
3824       safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3825       strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3826       strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3827       errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3828                                 &w, &h, pm, &x_hot, &y_hot);
3829       fprintf(stderr, "load %s\n", name);
3830         if (errcode != BitmapSuccess) {
3831             switch (errcode) {
3832               case BitmapOpenFailed:
3833                 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3834                 break;
3835               case BitmapFileInvalid:
3836                 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3837                 break;
3838               case BitmapNoMemory:
3839                 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3840                         fullname);
3841                 break;
3842               default:
3843                 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3844                         errcode, fullname);
3845                 break;
3846             }
3847             fprintf(stderr, _("%s: %s...using built-in\n"),
3848                     programName, msg);
3849         } else if (w != wreq || h != hreq) {
3850             fprintf(stderr,
3851                     _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3852                     programName, fullname, w, h, wreq, hreq);
3853         } else {
3854             return;
3855         }
3856     }
3857     if (bits != NULL) {
3858         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3859                                     wreq, hreq);
3860     }
3861 }
3862
3863 void
3864 CreateGrid ()
3865 {
3866     int i, j;
3867
3868     if (lineGap == 0) return;
3869
3870     /* [HR] Split this into 2 loops for non-square boards. */
3871
3872     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3873         gridSegments[i].x1 = 0;
3874         gridSegments[i].x2 =
3875           lineGap + BOARD_WIDTH * (squareSize + lineGap);
3876         gridSegments[i].y1 = gridSegments[i].y2
3877           = lineGap / 2 + (i * (squareSize + lineGap));
3878     }
3879
3880     for (j = 0; j < BOARD_WIDTH + 1; j++) {
3881         gridSegments[j + i].y1 = 0;
3882         gridSegments[j + i].y2 =
3883           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3884         gridSegments[j + i].x1 = gridSegments[j + i].x2
3885           = lineGap / 2 + (j * (squareSize + lineGap));
3886     }
3887 }
3888
3889 static void
3890 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3891 {
3892     XtActionProc proc = (XtActionProc) addr;
3893
3894     (proc)(NULL, NULL, NULL, NULL);
3895 }
3896
3897 static void
3898 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3899 {
3900     RecentEngineEvent((int) addr);
3901 }
3902
3903 void
3904 AppendEnginesToMenu (Widget menu, char *list)
3905 {
3906     int i=0, j;
3907     Widget entry;
3908     MenuItem *mi;
3909     Arg args[16];
3910     char *p;
3911
3912     if(appData.recentEngines <= 0) return;
3913     recentEngines = strdup(list);
3914     j = 0;
3915     XtSetArg(args[j], XtNleftMargin, 20);   j++;
3916     XtSetArg(args[j], XtNrightMargin, 20);  j++;
3917     while (*list) {
3918         p = strchr(list, '\n'); if(p == NULL) break;
3919         if(i == 0) XtCreateManagedWidget(_("----"), smeLineObjectClass, menu, args, j); // at least one valid item to add
3920         *p = 0;
3921         XtSetArg(args[j], XtNlabel, XtNewString(list));
3922         entry = XtCreateManagedWidget("engine", smeBSBObjectClass, menu, args, j+1);
3923         XtAddCallback(entry, XtNcallback,
3924                           (XtCallbackProc) MenuEngineSelect,
3925                           (caddr_t) i);
3926         i++; *p = '\n'; list = p + 1;
3927     }
3928 }
3929
3930 void
3931 CreateMenuBarPopup (Widget parent, String name, Menu *mb)
3932 {
3933     int j;
3934     Widget menu, entry;
3935     MenuItem *mi;
3936     Arg args[16];
3937
3938     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3939                               parent, NULL, 0);
3940     j = 0;
3941     XtSetArg(args[j], XtNleftMargin, 20);   j++;
3942     XtSetArg(args[j], XtNrightMargin, 20);  j++;
3943     mi = mb->mi;
3944     while (mi->string != NULL) {
3945         if (strcmp(mi->string, "----") == 0) {
3946           entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3947                                           menu, args, j);
3948         } else {
3949           XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3950             entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3951                                           menu, args, j+1);
3952             XtAddCallback(entry, XtNcallback,
3953                           (XtCallbackProc) MenuBarSelect,
3954                           (caddr_t) mi->proc);
3955         }
3956         mi++;
3957     }
3958     if(!strcmp(mb->name, "Engine")) AppendEnginesToMenu(menu, appData.recentEngineList);
3959 }
3960
3961 Widget
3962 CreateMenuBar (Menu *mb, int boardWidth)
3963 {
3964     int i, j, nr = 0, wtot = 0, widths[10];
3965     Widget menuBar;
3966     Arg args[16];
3967     char menuName[MSG_SIZ];
3968     Dimension w;
3969     Menu *ma = mb;
3970
3971     j = 0;
3972     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
3973     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3974     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3975     menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3976                              formWidget, args, j);
3977
3978     while (mb->name != NULL) {
3979         safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3980         strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3981         j = 0;
3982         XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
3983         XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name)));  j++;
3984         XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3985         mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3986                                        menuBar, args, j);
3987         CreateMenuBarPopup(menuBar, menuName, mb);
3988         j = 0;
3989         XtSetArg(args[j], XtNwidth, &w);                   j++;
3990         XtGetValues(mb->subMenu, args, j);
3991         wtot += mb->textWidth = widths[nr++] = w;
3992         mb++;
3993     }
3994     while(wtot > boardWidth - 40) {
3995         int wmax=0, imax=0;
3996         for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3997         widths[imax]--;
3998         wtot--;
3999     }
4000     for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
4001         j = 0;
4002         XtSetArg(args[j], XtNwidth, widths[i]);                   j++;
4003         XtSetValues(ma[i].subMenu, args, j);
4004     }
4005     return menuBar;
4006 }
4007
4008 Widget
4009 CreateButtonBar (MenuItem *mi)
4010 {
4011     int j;
4012     Widget button, buttonBar;
4013     Arg args[16];
4014
4015     j = 0;
4016     XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
4017     if (tinyLayout) {
4018         XtSetArg(args[j], XtNhSpace, 0); j++;
4019     }
4020     XtSetArg(args[j], XtNborderWidth, 0); j++;
4021     XtSetArg(args[j], XtNvSpace, 0);                        j++;
4022     buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
4023                                formWidget, args, j);
4024
4025     while (mi->string != NULL) {
4026         j = 0;
4027         if (tinyLayout) {
4028             XtSetArg(args[j], XtNinternalWidth, 2); j++;
4029             XtSetArg(args[j], XtNborderWidth, 0); j++;
4030         }
4031       XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
4032         button = XtCreateManagedWidget(mi->string, commandWidgetClass,
4033                                        buttonBar, args, j);
4034         XtAddCallback(button, XtNcallback,
4035                       (XtCallbackProc) MenuBarSelect,
4036                       (caddr_t) mi->proc);
4037         mi++;
4038     }
4039     return buttonBar;
4040 }
4041
4042 Widget
4043 CreatePieceMenu (char *name, int color)
4044 {
4045     int i;
4046     Widget entry, menu;
4047     Arg args[16];
4048     ChessSquare selection;
4049
4050     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4051                               boardWidget, args, 0);
4052
4053     for (i = 0; i < PIECE_MENU_SIZE; i++) {
4054         String item = pieceMenuStrings[color][i];
4055
4056         if (strcmp(item, "----") == 0) {
4057             entry = XtCreateManagedWidget(item, smeLineObjectClass,
4058                                           menu, NULL, 0);
4059         } else {
4060           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4061             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4062                                 menu, args, 1);
4063             selection = pieceMenuTranslation[color][i];
4064             XtAddCallback(entry, XtNcallback,
4065                           (XtCallbackProc) PieceMenuSelect,
4066                           (caddr_t) selection);
4067             if (selection == WhitePawn || selection == BlackPawn) {
4068                 XtSetArg(args[0], XtNpopupOnEntry, entry);
4069                 XtSetValues(menu, args, 1);
4070             }
4071         }
4072     }
4073     return menu;
4074 }
4075
4076 void
4077 CreatePieceMenus ()
4078 {
4079     int i;
4080     Widget entry;
4081     Arg args[16];
4082     ChessSquare selection;
4083
4084     whitePieceMenu = CreatePieceMenu("menuW", 0);
4085     blackPieceMenu = CreatePieceMenu("menuB", 1);
4086
4087     if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
4088     XtRegisterGrabAction(PieceMenuPopup, True,
4089                          (unsigned)(ButtonPressMask|ButtonReleaseMask),
4090                          GrabModeAsync, GrabModeAsync);
4091
4092     XtSetArg(args[0], XtNlabel, _("Drop"));
4093     dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4094                                   boardWidget, args, 1);
4095     for (i = 0; i < DROP_MENU_SIZE; i++) {
4096         String item = dropMenuStrings[i];
4097
4098         if (strcmp(item, "----") == 0) {
4099             entry = XtCreateManagedWidget(item, smeLineObjectClass,
4100                                           dropMenu, NULL, 0);
4101         } else {
4102           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4103             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4104                                 dropMenu, args, 1);
4105             selection = dropMenuTranslation[i];
4106             XtAddCallback(entry, XtNcallback,
4107                           (XtCallbackProc) DropMenuSelect,
4108                           (caddr_t) selection);
4109         }
4110     }
4111 }
4112
4113 void
4114 SetupDropMenu ()
4115 {
4116     int i, j, count;
4117     char label[32];
4118     Arg args[16];
4119     Widget entry;
4120     char* p;
4121
4122     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4123         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4124         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4125                    dmEnables[i].piece);
4126         XtSetSensitive(entry, p != NULL || !appData.testLegality
4127                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4128                                        && !appData.icsActive));
4129         count = 0;
4130         while (p && *p++ == dmEnables[i].piece) count++;
4131         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
4132         j = 0;
4133         XtSetArg(args[j], XtNlabel, label); j++;
4134         XtSetValues(entry, args, j);
4135     }
4136 }
4137
4138 void
4139 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
4140 {
4141     String whichMenu; int menuNr = -2;
4142     shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4143     if (event->type == ButtonRelease)
4144         menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4145     else if (event->type == ButtonPress)
4146         menuNr = RightClick(Press,   event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4147     switch(menuNr) {
4148       case 0: whichMenu = params[0]; break;
4149       case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4150       case 2:
4151       case -1: if (errorUp) ErrorPopDown();
4152       default: return;
4153     }
4154     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4155 }
4156
4157 static void
4158 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4159 {
4160     if (pmFromX < 0 || pmFromY < 0) return;
4161     EditPositionMenuEvent(piece, pmFromX, pmFromY);
4162 }
4163
4164 static void
4165 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4166 {
4167     if (pmFromX < 0 || pmFromY < 0) return;
4168     DropMenuEvent(piece, pmFromX, pmFromY);
4169 }
4170
4171 void
4172 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4173 {
4174     shiftKey = prms[0][0] & 1;
4175     ClockClick(0);
4176 }
4177
4178 void
4179 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4180 {
4181     shiftKey = prms[0][0] & 1;
4182     ClockClick(1);
4183 }
4184
4185
4186 /*
4187  * If the user selects on a border boundary, return -1; if off the board,
4188  *   return -2.  Otherwise map the event coordinate to the square.
4189  */
4190 int
4191 EventToSquare (int x, int limit)
4192 {
4193     if (x <= 0)
4194       return -2;
4195     if (x < lineGap)
4196       return -1;
4197     x -= lineGap;
4198     if ((x % (squareSize + lineGap)) >= squareSize)
4199       return -1;
4200     x /= (squareSize + lineGap);
4201     if (x >= limit)
4202       return -2;
4203     return x;
4204 }
4205
4206 static void
4207 do_flash_delay (unsigned long msec)
4208 {
4209     TimeDelay(msec);
4210 }
4211
4212 static void
4213 drawHighlight (int file, int rank, GC gc)
4214 {
4215     int x, y;
4216
4217     if (lineGap == 0) return;
4218
4219     if (flipView) {
4220         x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4221           (squareSize + lineGap);
4222         y = lineGap/2 + rank * (squareSize + lineGap);
4223     } else {
4224         x = lineGap/2 + file * (squareSize + lineGap);
4225         y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4226           (squareSize + lineGap);
4227     }
4228
4229     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4230                    squareSize+lineGap, squareSize+lineGap);
4231 }
4232
4233 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4234 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4235
4236 void
4237 SetHighlights (int fromX, int fromY, int toX, int toY)
4238 {
4239     if (hi1X != fromX || hi1Y != fromY) {
4240         if (hi1X >= 0 && hi1Y >= 0) {
4241             drawHighlight(hi1X, hi1Y, lineGC);
4242         }
4243     } // [HGM] first erase both, then draw new!
4244     if (hi2X != toX || hi2Y != toY) {
4245         if (hi2X >= 0 && hi2Y >= 0) {
4246             drawHighlight(hi2X, hi2Y, lineGC);
4247         }
4248     }
4249     if (hi1X != fromX || hi1Y != fromY) {
4250         if (fromX >= 0 && fromY >= 0) {
4251             drawHighlight(fromX, fromY, highlineGC);
4252         }
4253     }
4254     if (hi2X != toX || hi2Y != toY) {
4255         if (toX >= 0 && toY >= 0) {
4256             drawHighlight(toX, toY, highlineGC);
4257         }
4258     }
4259     if(toX<0) // clearing the highlights must have damaged arrow
4260         DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
4261     hi1X = fromX;
4262     hi1Y = fromY;
4263     hi2X = toX;
4264     hi2Y = toY;
4265 }
4266
4267 void
4268 ClearHighlights ()
4269 {
4270     SetHighlights(-1, -1, -1, -1);
4271 }
4272
4273
4274 void
4275 SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
4276 {
4277     if (pm1X != fromX || pm1Y != fromY) {