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