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