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