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