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