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