4b7f3feb74f04e258bc3a1a23cd14742171b09e2
[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 {
3666     DrawPosition(True, NULL);
3667 }
3668
3669
3670 /*
3671  * event handler for parsing user moves
3672  */
3673 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3674 //       way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3675 //       it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3676 //       should be made to use the new way, of calling UserMoveTest early  to determine the legality of the
3677 //       move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3678 //       and at the end FinishMove() to perform the move after optional promotion popups.
3679 //       For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3680 void
3681 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3682 {
3683     if (w != boardWidget || errorExitStatus != -1) return;
3684     if(nprms) shiftKey = !strcmp(prms[0], "1");
3685
3686     if (promotionUp) {
3687         if (event->type == ButtonPress) {
3688             XtPopdown(promotionShell);
3689             XtDestroyWidget(promotionShell);
3690             promotionUp = False;
3691             ClearHighlights();
3692             fromX = fromY = -1;
3693         } else {
3694             return;
3695         }
3696     }
3697
3698     // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3699     if(event->type == ButtonPress)   LeftClick(Press,   event->xbutton.x, event->xbutton.y);
3700     if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3701 }
3702
3703 void
3704 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3705 {
3706     if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3707     DragPieceMove(event->xmotion.x, event->xmotion.y);
3708 }
3709
3710 void
3711 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3712 {   // [HGM] pv: walk PV
3713     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3714 }
3715
3716 static int savedIndex;  /* gross that this is global */
3717
3718 void
3719 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3720 {
3721         String val;
3722         XawTextPosition index, dummy;
3723         Arg arg;
3724
3725         XawTextGetSelectionPos(w, &index, &dummy);
3726         XtSetArg(arg, XtNstring, &val);
3727         XtGetValues(w, &arg, 1);
3728         ReplaceComment(savedIndex, val);
3729         if(savedIndex != currentMove) ToNrEvent(savedIndex);
3730         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3731 }
3732
3733 void
3734 EditCommentPopUp (int index, char *title, char *text)
3735 {
3736     savedIndex = index;
3737     if (text == NULL) text = "";
3738     NewCommentPopup(title, text, index);
3739 }
3740
3741 void
3742 CommentPopUp (char *title, char *text)
3743 {
3744     savedIndex = currentMove; // [HGM] vari
3745     NewCommentPopup(title, text, currentMove);
3746 }
3747
3748 void
3749 CommentPopDown ()
3750 {
3751     PopDown(CommentDlg);
3752 }
3753
3754 static char *openName;
3755 FILE *openFP;
3756
3757 void
3758 DelayedLoad ()
3759 {
3760   (void) (*fileProc)(openFP, 0, openName);
3761 }
3762
3763 void
3764 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3765 {
3766     fileProc = proc;            /* I can't see a way not */
3767     fileOpenMode = openMode;    /*   to use globals here */
3768     {   // [HGM] use file-selector dialog stolen from Ghostview
3769         int index; // this is not supported yet
3770         if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3771                            (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3772           // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3773           ScheduleDelayedEvent(&DelayedLoad, 50);
3774     }
3775 }
3776
3777 void
3778 PromotionPopUp ()
3779 {
3780     Arg args[16];
3781     Widget dialog, layout;
3782     Position x, y;
3783     Dimension bw_width, pw_width;
3784     int j;
3785     char *PromoChars = "wglcqrbnkac+=\0";
3786
3787     j = 0;
3788     XtSetArg(args[j], XtNwidth, &bw_width); j++;
3789     XtGetValues(boardWidget, args, j);
3790
3791     j = 0;
3792     XtSetArg(args[j], XtNresizable, True); j++;
3793     XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3794     promotionShell =
3795       XtCreatePopupShell("Promotion", transientShellWidgetClass,
3796                          shellWidget, args, j);
3797     layout =
3798       XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3799                             layoutArgs, XtNumber(layoutArgs));
3800
3801     j = 0;
3802     XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3803     XtSetArg(args[j], XtNborderWidth, 0); j++;
3804     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3805                                    layout, args, j);
3806
3807   if(gameInfo.variant != VariantShogi) {
3808    if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
3809       XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
3810       XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
3811       XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
3812       XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
3813     } else {
3814     XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
3815     XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
3816     XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
3817     XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
3818     }
3819     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3820         gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
3821         gameInfo.variant == VariantGiveaway) {
3822       XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
3823     }
3824     if(gameInfo.variant == VariantCapablanca ||
3825        gameInfo.variant == VariantGothic ||
3826        gameInfo.variant == VariantCapaRandom) {
3827       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
3828       XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
3829     }
3830   } else // [HGM] shogi
3831   {
3832       XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
3833       XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
3834   }
3835     XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
3836
3837     XtRealizeWidget(promotionShell);
3838     CatchDeleteWindow(promotionShell, "PromotionPopDown");
3839
3840     j = 0;
3841     XtSetArg(args[j], XtNwidth, &pw_width); j++;
3842     XtGetValues(promotionShell, args, j);
3843
3844     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3845                       lineGap + squareSize/3 +
3846                       ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3847                        0 : 6*(squareSize + lineGap)), &x, &y);
3848
3849     j = 0;
3850     XtSetArg(args[j], XtNx, x); j++;
3851     XtSetArg(args[j], XtNy, y); j++;
3852     XtSetValues(promotionShell, args, j);
3853
3854     XtPopup(promotionShell, XtGrabNone);
3855
3856     promotionUp = True;
3857 }
3858
3859 void
3860 PromotionPopDown ()
3861 {
3862     if (!promotionUp) return;
3863     XtPopdown(promotionShell);
3864     XtDestroyWidget(promotionShell);
3865     promotionUp = False;
3866 }
3867
3868 void
3869 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
3870 {
3871     int promoChar = * (const char *) client_data;
3872
3873     PromotionPopDown();
3874
3875     if (fromX == -1) return;
3876
3877     if (! promoChar) {
3878         fromX = fromY = -1;
3879         ClearHighlights();
3880         return;
3881     }
3882     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3883
3884     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3885     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3886     fromX = fromY = -1;
3887 }
3888
3889
3890 void
3891 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
3892 {
3893     dialogError = errorUp = False;
3894     XtPopdown(w = XtParent(XtParent(XtParent(w))));
3895     XtDestroyWidget(w);
3896     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3897 }
3898
3899
3900 void
3901 ErrorPopDown ()
3902 {
3903     if (!errorUp) return;
3904     dialogError = errorUp = False;
3905     XtPopdown(errorShell);
3906     XtDestroyWidget(errorShell);
3907     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3908 }
3909
3910 void
3911 ErrorPopUp (char *title, char *label, int modal)
3912 {
3913     Arg args[16];
3914     Widget dialog, layout;
3915     Position x, y;
3916     int xx, yy;
3917     Window junk;
3918     Dimension bw_width, pw_width;
3919     Dimension pw_height;
3920     int i;
3921
3922     i = 0;
3923     XtSetArg(args[i], XtNresizable, True);  i++;
3924     XtSetArg(args[i], XtNtitle, title); i++;
3925     errorShell =
3926       XtCreatePopupShell("errorpopup", transientShellWidgetClass,
3927                          shellUp[TransientDlg] ? (dialogError = modal = TRUE, shells[TransientDlg]) : shellWidget, args, i);
3928     layout =
3929       XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
3930                             layoutArgs, XtNumber(layoutArgs));
3931
3932     i = 0;
3933     XtSetArg(args[i], XtNlabel, label); i++;
3934     XtSetArg(args[i], XtNborderWidth, 0); i++;
3935     dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
3936                                    layout, args, i);
3937
3938     XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
3939
3940     XtRealizeWidget(errorShell);
3941     CatchDeleteWindow(errorShell, "ErrorPopDown");
3942
3943     i = 0;
3944     XtSetArg(args[i], XtNwidth, &bw_width);  i++;
3945     XtGetValues(boardWidget, args, i);
3946     i = 0;
3947     XtSetArg(args[i], XtNwidth, &pw_width);  i++;
3948     XtSetArg(args[i], XtNheight, &pw_height);  i++;
3949     XtGetValues(errorShell, args, i);
3950
3951 #ifdef NOTDEF
3952     /* This code seems to tickle an X bug if it is executed too soon
3953        after xboard starts up.  The coordinates get transformed as if
3954        the main window was positioned at (0, 0).
3955        */
3956     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3957                       0 - pw_height + squareSize / 3, &x, &y);
3958 #else
3959     XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
3960                           RootWindowOfScreen(XtScreen(boardWidget)),
3961                           (bw_width - pw_width) / 2,
3962                           0 - pw_height + squareSize / 3, &xx, &yy, &junk);
3963     x = xx;
3964     y = yy;
3965 #endif
3966     if (y < 0) y = 0; /*avoid positioning top offscreen*/
3967
3968     i = 0;
3969     XtSetArg(args[i], XtNx, x);  i++;
3970     XtSetArg(args[i], XtNy, y);  i++;
3971     XtSetValues(errorShell, args, i);
3972
3973     errorUp = True;
3974     XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
3975 }
3976
3977 /* Disable all user input other than deleting the window */
3978 static int frozen = 0;
3979
3980 void
3981 FreezeUI ()
3982 {
3983   if (frozen) return;
3984   /* Grab by a widget that doesn't accept input */
3985   XtAddGrab(messageWidget, TRUE, FALSE);
3986   frozen = 1;
3987 }
3988
3989 /* Undo a FreezeUI */
3990 void
3991 ThawUI ()
3992 {
3993   if (!frozen) return;
3994   XtRemoveGrab(messageWidget);
3995   frozen = 0;
3996 }
3997
3998 void
3999 ModeHighlight ()
4000 {
4001     Arg args[16];
4002     static int oldPausing = FALSE;
4003     static GameMode oldmode = (GameMode) -1;
4004     char *wname;
4005
4006     if (!boardWidget || !XtIsRealized(boardWidget)) return;
4007
4008     if (pausing != oldPausing) {
4009         oldPausing = pausing;
4010         MarkMenuItem("Pause", pausing);
4011
4012         if (appData.showButtonBar) {
4013           /* Always toggle, don't set.  Previous code messes up when
4014              invoked while the button is pressed, as releasing it
4015              toggles the state again. */
4016           {
4017             Pixel oldbg, oldfg;
4018             XtSetArg(args[0], XtNbackground, &oldbg);
4019             XtSetArg(args[1], XtNforeground, &oldfg);
4020             XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
4021                         args, 2);
4022             XtSetArg(args[0], XtNbackground, oldfg);
4023             XtSetArg(args[1], XtNforeground, oldbg);
4024           }
4025           XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
4026         }
4027     }
4028
4029     wname = ModeToWidgetName(oldmode);
4030     if (wname != NULL) {
4031         MarkMenuItem(wname, False);
4032     }
4033     wname = ModeToWidgetName(gameMode);
4034     if (wname != NULL) {
4035         MarkMenuItem(wname, True);
4036     }
4037     oldmode = gameMode;
4038     MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
4039
4040     /* Maybe all the enables should be handled here, not just this one */
4041     EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
4042 }
4043
4044
4045 /*
4046  * Button/menu procedures
4047  */
4048 int
4049 LoadGamePopUp (FILE *f, int gameNumber, char *title)
4050 {
4051     cmailMsgLoaded = FALSE;
4052     if (gameNumber == 0) {
4053         int error = GameListBuild(f);
4054         if (error) {
4055             DisplayError(_("Cannot build game list"), error);
4056         } else if (!ListEmpty(&gameList) &&
4057                    ((ListGame *) gameList.tailPred)->number > 1) {
4058             GameListPopUp(f, title);
4059             return TRUE;
4060         }
4061         GameListDestroy();
4062         gameNumber = 1;
4063     }
4064     return LoadGame(f, gameNumber, title, FALSE);
4065 }
4066
4067 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4068 char *selected_fen_position=NULL;
4069
4070 Boolean
4071 SendPositionSelection (Widget w, Atom *selection, Atom *target,
4072                        Atom *type_return, XtPointer *value_return,
4073                        unsigned long *length_return, int *format_return)
4074 {
4075   char *selection_tmp;
4076
4077 //  if (!selected_fen_position) return False; /* should never happen */
4078   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4079    if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
4080     FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
4081     long len;
4082     size_t count;
4083     if (f == NULL) return False;
4084     fseek(f, 0, 2);
4085     len = ftell(f);
4086     rewind(f);
4087     selection_tmp = XtMalloc(len + 1);
4088     count = fread(selection_tmp, 1, len, f);
4089     fclose(f);
4090     if (len != count) {
4091       XtFree(selection_tmp);
4092       return False;
4093     }
4094     selection_tmp[len] = NULLCHAR;
4095    } else {
4096     /* note: since no XtSelectionDoneProc was registered, Xt will
4097      * automatically call XtFree on the value returned.  So have to
4098      * make a copy of it allocated with XtMalloc */
4099     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4100     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
4101    }
4102
4103     *value_return=selection_tmp;
4104     *length_return=strlen(selection_tmp);
4105     *type_return=*target;
4106     *format_return = 8; /* bits per byte */
4107     return True;
4108   } else if (*target == XA_TARGETS(xDisplay)) {
4109     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4110     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4111     targets_tmp[1] = XA_STRING;
4112     *value_return = targets_tmp;
4113     *type_return = XA_ATOM;
4114     *length_return = 2;
4115 #if 0
4116     // This code leads to a read of value_return out of bounds on 64-bit systems.
4117     // Other code which I have seen always sets *format_return to 32 independent of
4118     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4119     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4120     *format_return = 8 * sizeof(Atom);
4121     if (*format_return > 32) {
4122       *length_return *= *format_return / 32;
4123       *format_return = 32;
4124     }
4125 #else
4126     *format_return = 32;
4127 #endif
4128     return True;
4129   } else {
4130     return False;
4131   }
4132 }
4133
4134 /* note: when called from menu all parameters are NULL, so no clue what the
4135  * Widget which was clicked on was, or what the click event was
4136  */
4137 void
4138 CopySomething (char *src)
4139 {
4140     selected_fen_position = src;
4141     /*
4142      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4143      * have a notion of a position that is selected but not copied.
4144      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4145      */
4146     XtOwnSelection(menuBarWidget, XA_PRIMARY,
4147                    CurrentTime,
4148                    SendPositionSelection,
4149                    NULL/* lose_ownership_proc */ ,
4150                    NULL/* transfer_done_proc */);
4151     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4152                    CurrentTime,
4153                    SendPositionSelection,
4154                    NULL/* lose_ownership_proc */ ,
4155                    NULL/* transfer_done_proc */);
4156 }
4157
4158 /* function called when the data to Paste is ready */
4159 static void
4160 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
4161                  Atom *type, XtPointer value, unsigned long *len, int *format)
4162 {
4163   char *fenstr=value;
4164   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4165   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4166   EditPositionPasteFEN(fenstr);
4167   XtFree(value);
4168 }
4169
4170 /* called when Paste Position button is pressed,
4171  * all parameters will be NULL */
4172 void
4173 PastePositionProc ()
4174 {
4175     XtGetSelectionValue(menuBarWidget,
4176       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4177       /* (XtSelectionCallbackProc) */ PastePositionCB,
4178       NULL, /* client_data passed to PastePositionCB */
4179
4180       /* better to use the time field from the event that triggered the
4181        * call to this function, but that isn't trivial to get
4182        */
4183       CurrentTime
4184     );
4185     return;
4186 }
4187
4188 /* note: when called from menu all parameters are NULL, so no clue what the
4189  * Widget which was clicked on was, or what the click event was
4190  */
4191 /* function called when the data to Paste is ready */
4192 static void
4193 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
4194              Atom *type, XtPointer value, unsigned long *len, int *format)
4195 {
4196   FILE* f;
4197   if (value == NULL || *len == 0) {
4198     return; /* nothing had been selected to copy */
4199   }
4200   f = fopen(gamePasteFilename, "w");
4201   if (f == NULL) {
4202     DisplayError(_("Can't open temp file"), errno);
4203     return;
4204   }
4205   fwrite(value, 1, *len, f);
4206   fclose(f);
4207   XtFree(value);
4208   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4209 }
4210
4211 /* called when Paste Game button is pressed,
4212  * all parameters will be NULL */
4213 void
4214 PasteGameProc ()
4215 {
4216     XtGetSelectionValue(menuBarWidget,
4217       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4218       /* (XtSelectionCallbackProc) */ PasteGameCB,
4219       NULL, /* client_data passed to PasteGameCB */
4220
4221       /* better to use the time field from the event that triggered the
4222        * call to this function, but that isn't trivial to get
4223        */
4224       CurrentTime
4225     );
4226     return;
4227 }
4228
4229
4230 void
4231 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4232 {
4233     QuitProc();
4234 }
4235
4236 int
4237 ShiftKeys ()
4238 {   // bassic primitive for determining if modifier keys are pressed
4239     long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
4240     char keys[32];
4241     int i,j,  k=0;
4242     XQueryKeymap(xDisplay,keys);
4243     for(i=0; i<6; i++) {
4244         k <<= 1;
4245         j = XKeysymToKeycode(xDisplay, codes[i]);
4246         k += ( (keys[j>>3]&1<<(j&7)) != 0 );
4247     }
4248     return k;
4249 }
4250
4251 static void
4252 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
4253 {
4254     char buf[10];
4255     KeySym sym;
4256     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
4257     if ( n == 1 && *buf >= 32 // printable
4258          && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
4259         ) BoxAutoPopUp (buf);
4260 }
4261
4262 static void
4263 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4264 {   // [HGM] input: let up-arrow recall previous line from history
4265     IcsKey(1);
4266 }
4267
4268 static void
4269 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4270 {   // [HGM] input: let down-arrow recall next line from history
4271     IcsKey(-1);
4272 }
4273
4274 static void
4275 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4276 {
4277     IcsKey(0);
4278 }
4279
4280 void
4281 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4282 {
4283         if (!TempBackwardActive) {
4284                 TempBackwardActive = True;
4285                 BackwardEvent();
4286         }
4287 }
4288
4289 void
4290 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4291 {
4292         /* Check to see if triggered by a key release event for a repeating key.
4293          * If so the next queued event will be a key press of the same key at the same time */
4294         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
4295                 XEvent next;
4296                 XPeekEvent(xDisplay, &next);
4297                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
4298                         next.xkey.keycode == event->xkey.keycode)
4299                                 return;
4300         }
4301     ForwardEvent();
4302         TempBackwardActive = False;
4303 }
4304
4305 void
4306 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4307 {   // called as key binding
4308     char buf[MSG_SIZ];
4309     String name;
4310     if (nprms && *nprms > 0)
4311       name = prms[0];
4312     else
4313       name = "xboard";
4314     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
4315     system(buf);
4316 }
4317
4318 void
4319 DisplayMessage (char *message, char *extMessage)
4320 {
4321   /* display a message in the message widget */
4322
4323   char buf[MSG_SIZ];
4324   Arg arg;
4325
4326   if (extMessage)
4327     {
4328       if (*message)
4329         {
4330           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
4331           message = buf;
4332         }
4333       else
4334         {
4335           message = extMessage;
4336         };
4337     };
4338
4339     safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
4340
4341   /* need to test if messageWidget already exists, since this function
4342      can also be called during the startup, if for example a Xresource
4343      is not set up correctly */
4344   if(messageWidget)
4345     {
4346       XtSetArg(arg, XtNlabel, message);
4347       XtSetValues(messageWidget, &arg, 1);
4348     };
4349
4350   return;
4351 }
4352
4353 void
4354 SetWindowTitle (char *text, char *title, char *icon)
4355 {
4356     Arg args[16];
4357     int i;
4358     if (appData.titleInWindow) {
4359         i = 0;
4360         XtSetArg(args[i], XtNlabel, text);   i++;
4361         XtSetValues(titleWidget, args, i);
4362     }
4363     i = 0;
4364     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
4365     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
4366     XtSetValues(shellWidget, args, i);
4367     XSync(xDisplay, False);
4368 }
4369
4370
4371 static int
4372 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
4373 {
4374     return 0;
4375 }
4376
4377 void
4378 DisplayIcsInteractionTitle (String message)
4379 {
4380   if (oldICSInteractionTitle == NULL) {
4381     /* Magic to find the old window title, adapted from vim */
4382     char *wina = getenv("WINDOWID");
4383     if (wina != NULL) {
4384       Window win = (Window) atoi(wina);
4385       Window root, parent, *children;
4386       unsigned int nchildren;
4387       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4388       for (;;) {
4389         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4390         if (!XQueryTree(xDisplay, win, &root, &parent,
4391                         &children, &nchildren)) break;
4392         if (children) XFree((void *)children);
4393         if (parent == root || parent == 0) break;
4394         win = parent;
4395       }
4396       XSetErrorHandler(oldHandler);
4397     }
4398     if (oldICSInteractionTitle == NULL) {
4399       oldICSInteractionTitle = "xterm";
4400     }
4401   }
4402   printf("\033]0;%s\007", message);
4403   fflush(stdout);
4404 }
4405
4406 char pendingReplyPrefix[MSG_SIZ];
4407 ProcRef pendingReplyPR;
4408
4409 void
4410 AskQuestionPopDown ()
4411 {
4412     if (!askQuestionUp) return;
4413     XtPopdown(askQuestionShell);
4414     XtDestroyWidget(askQuestionShell);
4415     askQuestionUp = False;
4416 }
4417
4418 void
4419 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4420 {
4421     char buf[MSG_SIZ];
4422     int err;
4423     String reply;
4424
4425     reply = XawDialogGetValueString(w = XtParent(w));
4426     safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
4427     if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
4428     strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
4429     strncat(buf, "\n",  MSG_SIZ - strlen(buf) - 1);
4430     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4431     AskQuestionPopDown();
4432
4433     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4434 }
4435
4436 void
4437 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4438 {
4439     String name;
4440     Arg args[16];
4441
4442     XtSetArg(args[0], XtNlabel, &name);
4443     XtGetValues(w, args, 1);
4444
4445     if (strcmp(name, _("cancel")) == 0) {
4446         AskQuestionPopDown();
4447     } else {
4448         AskQuestionReplyAction(w, NULL, NULL, NULL);
4449     }
4450 }
4451
4452 void
4453 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
4454 {
4455     Arg args[16];
4456     Widget popup, layout, dialog, edit;
4457     Window root, child;
4458     int x, y, i;
4459     int win_x, win_y;
4460     unsigned int mask;
4461
4462     safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
4463     pendingReplyPR = pr;
4464
4465     i = 0;
4466     XtSetArg(args[i], XtNresizable, True); i++;
4467     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4468     askQuestionShell = popup =
4469       XtCreatePopupShell(title, transientShellWidgetClass,
4470                          shellWidget, args, i);
4471
4472     layout =
4473       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4474                             layoutArgs, XtNumber(layoutArgs));
4475
4476     i = 0;
4477     XtSetArg(args[i], XtNlabel, question); i++;
4478     XtSetArg(args[i], XtNvalue, ""); i++;
4479     XtSetArg(args[i], XtNborderWidth, 0); i++;
4480     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4481                                    layout, args, i);
4482
4483     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4484                        (XtPointer) dialog);
4485     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4486                        (XtPointer) dialog);
4487
4488     XtRealizeWidget(popup);
4489     CatchDeleteWindow(popup, "AskQuestionPopDown");
4490
4491     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4492                   &x, &y, &win_x, &win_y, &mask);
4493
4494     XtSetArg(args[0], XtNx, x - 10);
4495     XtSetArg(args[1], XtNy, y - 30);
4496     XtSetValues(popup, args, 2);
4497
4498     XtPopup(popup, XtGrabExclusive);
4499     askQuestionUp = True;
4500
4501     edit = XtNameToWidget(dialog, "*value");
4502     XtSetKeyboardFocus(popup, edit);
4503 }
4504
4505
4506 void
4507 PlaySound (char *name)
4508 {
4509   if (*name == NULLCHAR) {
4510     return;
4511   } else if (strcmp(name, "$") == 0) {
4512     putc(BELLCHAR, stderr);
4513   } else {
4514     char buf[2048];
4515     char *prefix = "", *sep = "";
4516     if(appData.soundProgram[0] == NULLCHAR) return;
4517     if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
4518     snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
4519     system(buf);
4520   }
4521 }
4522
4523 void
4524 RingBell ()
4525 {
4526   PlaySound(appData.soundMove);
4527 }
4528
4529 void
4530 PlayIcsWinSound ()
4531 {
4532   PlaySound(appData.soundIcsWin);
4533 }
4534
4535 void
4536 PlayIcsLossSound ()
4537 {
4538   PlaySound(appData.soundIcsLoss);
4539 }
4540
4541 void
4542 PlayIcsDrawSound ()
4543 {
4544   PlaySound(appData.soundIcsDraw);
4545 }
4546
4547 void
4548 PlayIcsUnfinishedSound ()
4549 {
4550   PlaySound(appData.soundIcsUnfinished);
4551 }
4552
4553 void
4554 PlayAlarmSound ()
4555 {
4556   PlaySound(appData.soundIcsAlarm);
4557 }
4558
4559 void
4560 PlayTellSound ()
4561 {
4562   PlaySound(appData.soundTell);
4563 }
4564
4565 void
4566 EchoOn ()
4567 {
4568     system("stty echo");
4569     noEcho = False;
4570 }
4571
4572 void
4573 EchoOff ()
4574 {
4575     system("stty -echo");
4576     noEcho = True;
4577 }
4578
4579 void
4580 RunCommand (char *buf)
4581 {
4582     system(buf);
4583 }
4584
4585 void
4586 Colorize (ColorClass cc, int continuation)
4587 {
4588     char buf[MSG_SIZ];
4589     int count, outCount, error;
4590
4591     if (textColors[(int)cc].bg > 0) {
4592         if (textColors[(int)cc].fg > 0) {
4593           snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4594                    textColors[(int)cc].fg, textColors[(int)cc].bg);
4595         } else {
4596           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4597                    textColors[(int)cc].bg);
4598         }
4599     } else {
4600         if (textColors[(int)cc].fg > 0) {
4601           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4602                     textColors[(int)cc].fg);
4603         } else {
4604           snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
4605         }
4606     }
4607     count = strlen(buf);
4608     outCount = OutputToProcess(NoProc, buf, count, &error);
4609     if (outCount < count) {
4610         DisplayFatalError(_("Error writing to display"), error, 1);
4611     }
4612
4613     if (continuation) return;
4614     switch (cc) {
4615     case ColorShout:
4616       PlaySound(appData.soundShout);
4617       break;
4618     case ColorSShout:
4619       PlaySound(appData.soundSShout);
4620       break;
4621     case ColorChannel1:
4622       PlaySound(appData.soundChannel1);
4623       break;
4624     case ColorChannel:
4625       PlaySound(appData.soundChannel);
4626       break;
4627     case ColorKibitz:
4628       PlaySound(appData.soundKibitz);
4629       break;
4630     case ColorTell:
4631       PlaySound(appData.soundTell);
4632       break;
4633     case ColorChallenge:
4634       PlaySound(appData.soundChallenge);
4635       break;
4636     case ColorRequest:
4637       PlaySound(appData.soundRequest);
4638       break;
4639     case ColorSeek:
4640       PlaySound(appData.soundSeek);
4641       break;
4642     case ColorNormal:
4643     case ColorNone:
4644     default:
4645       break;
4646     }
4647 }
4648
4649 char *
4650 UserName ()
4651 {
4652     return getpwuid(getuid())->pw_name;
4653 }
4654
4655 static char *
4656 ExpandPathName (char *path)
4657 {
4658     static char static_buf[4*MSG_SIZ];
4659     char *d, *s, buf[4*MSG_SIZ];
4660     struct passwd *pwd;
4661
4662     s = path;
4663     d = static_buf;
4664
4665     while (*s && isspace(*s))
4666       ++s;
4667
4668     if (!*s) {
4669         *d = 0;
4670         return static_buf;
4671     }
4672
4673     if (*s == '~') {
4674         if (*(s+1) == '/') {
4675           safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
4676           strcat(d, s+1);
4677         }
4678         else {
4679           safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
4680           { char *p; if(p = strchr(buf, '/')) *p = 0; }
4681           pwd = getpwnam(buf);
4682           if (!pwd)
4683             {
4684               fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
4685                       buf, path);
4686               return NULL;
4687             }
4688           safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
4689           strcat(d, strchr(s+1, '/'));
4690         }
4691     }
4692     else
4693       safeStrCpy(d, s, 4*MSG_SIZ );
4694
4695     return static_buf;
4696 }
4697
4698 char *
4699 HostName ()
4700 {
4701     static char host_name[MSG_SIZ];
4702
4703 #if HAVE_GETHOSTNAME
4704     gethostname(host_name, MSG_SIZ);
4705     return host_name;
4706 #else  /* not HAVE_GETHOSTNAME */
4707 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
4708     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
4709     return host_name;
4710 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4711     return "localhost";
4712 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4713 #endif /* not HAVE_GETHOSTNAME */
4714 }
4715
4716 XtIntervalId delayedEventTimerXID = 0;
4717 DelayedEventCallback delayedEventCallback = 0;
4718
4719 void
4720 FireDelayedEvent ()
4721 {
4722     delayedEventTimerXID = 0;
4723     delayedEventCallback();
4724 }
4725
4726 void
4727 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
4728 {
4729     if(delayedEventTimerXID && delayedEventCallback == cb)
4730         // [HGM] alive: replace, rather than add or flush identical event
4731         XtRemoveTimeOut(delayedEventTimerXID);
4732     delayedEventCallback = cb;
4733     delayedEventTimerXID =
4734       XtAppAddTimeOut(appContext, millisec,
4735                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
4736 }
4737
4738 DelayedEventCallback
4739 GetDelayedEvent ()
4740 {
4741   if (delayedEventTimerXID) {
4742     return delayedEventCallback;
4743   } else {
4744     return NULL;
4745   }
4746 }
4747
4748 void
4749 CancelDelayedEvent ()
4750 {
4751   if (delayedEventTimerXID) {
4752     XtRemoveTimeOut(delayedEventTimerXID);
4753     delayedEventTimerXID = 0;
4754   }
4755 }
4756
4757 XtIntervalId loadGameTimerXID = 0;
4758
4759 int
4760 LoadGameTimerRunning ()
4761 {
4762     return loadGameTimerXID != 0;
4763 }
4764
4765 int
4766 StopLoadGameTimer ()
4767 {
4768     if (loadGameTimerXID != 0) {
4769         XtRemoveTimeOut(loadGameTimerXID);
4770         loadGameTimerXID = 0;
4771         return TRUE;
4772     } else {
4773         return FALSE;
4774     }
4775 }
4776
4777 void
4778 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
4779 {
4780     loadGameTimerXID = 0;
4781     AutoPlayGameLoop();
4782 }
4783
4784 void
4785 StartLoadGameTimer (long millisec)
4786 {
4787     loadGameTimerXID =
4788       XtAppAddTimeOut(appContext, millisec,
4789                       (XtTimerCallbackProc) LoadGameTimerCallback,
4790                       (XtPointer) 0);
4791 }
4792
4793 XtIntervalId analysisClockXID = 0;
4794
4795 void
4796 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
4797 {
4798     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4799          || appData.icsEngineAnalyze) { // [DM]
4800         AnalysisPeriodicEvent(0);
4801         StartAnalysisClock();
4802     }
4803 }
4804
4805 void
4806 StartAnalysisClock ()
4807 {
4808     analysisClockXID =
4809       XtAppAddTimeOut(appContext, 2000,
4810                       (XtTimerCallbackProc) AnalysisClockCallback,
4811                       (XtPointer) 0);
4812 }
4813
4814 XtIntervalId clockTimerXID = 0;
4815
4816 int
4817 ClockTimerRunning ()
4818 {
4819     return clockTimerXID != 0;
4820 }
4821
4822 int
4823 StopClockTimer ()
4824 {
4825     if (clockTimerXID != 0) {
4826         XtRemoveTimeOut(clockTimerXID);
4827         clockTimerXID = 0;
4828         return TRUE;
4829     } else {
4830         return FALSE;
4831     }
4832 }
4833
4834 void
4835 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
4836 {
4837     clockTimerXID = 0;
4838     DecrementClocks();
4839 }
4840
4841 void
4842 StartClockTimer (long millisec)
4843 {
4844     clockTimerXID =
4845       XtAppAddTimeOut(appContext, millisec,
4846                       (XtTimerCallbackProc) ClockTimerCallback,
4847                       (XtPointer) 0);
4848 }
4849
4850 void
4851 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
4852 {
4853     char buf[MSG_SIZ];
4854     Arg args[16];
4855
4856     /* check for low time warning */
4857     Pixel foregroundOrWarningColor = timerForegroundPixel;
4858
4859     if (timer > 0 &&
4860         appData.lowTimeWarning &&
4861         (timer / 1000) < appData.icsAlarmTime)
4862       foregroundOrWarningColor = lowTimeWarningColor;
4863
4864     if (appData.clockMode) {
4865       snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
4866       XtSetArg(args[0], XtNlabel, buf);
4867     } else {
4868       snprintf(buf, MSG_SIZ, "%s  ", color);
4869       XtSetArg(args[0], XtNlabel, buf);
4870     }
4871
4872     if (highlight) {
4873
4874         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
4875         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
4876     } else {
4877         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
4878         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
4879     }
4880
4881     XtSetValues(w, args, 3);
4882 }
4883
4884 void
4885 DisplayWhiteClock (long timeRemaining, int highlight)
4886 {
4887     Arg args[16];
4888
4889     if(appData.noGUI) return;
4890     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
4891     if (highlight && iconPixmap == bIconPixmap) {
4892         iconPixmap = wIconPixmap;
4893         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4894         XtSetValues(shellWidget, args, 1);
4895     }
4896 }
4897
4898 void
4899 DisplayBlackClock (long timeRemaining, int highlight)
4900 {
4901     Arg args[16];
4902
4903     if(appData.noGUI) return;
4904     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
4905     if (highlight && iconPixmap == wIconPixmap) {
4906         iconPixmap = bIconPixmap;
4907         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4908         XtSetValues(shellWidget, args, 1);
4909     }
4910 }
4911
4912 #define CPNone 0
4913 #define CPReal 1
4914 #define CPComm 2
4915 #define CPSock 3
4916 #define CPLoop 4
4917 typedef int CPKind;
4918
4919 typedef struct {
4920     CPKind kind;
4921     int pid;
4922     int fdTo, fdFrom;
4923 } ChildProc;
4924
4925
4926 int
4927 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
4928 {
4929     char *argv[64], *p;
4930     int i, pid;
4931     int to_prog[2], from_prog[2];
4932     ChildProc *cp;
4933     char buf[MSG_SIZ];
4934
4935     if (appData.debugMode) {
4936         fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
4937     }
4938
4939     /* We do NOT feed the cmdLine to the shell; we just
4940        parse it into blank-separated arguments in the
4941        most simple-minded way possible.
4942        */
4943     i = 0;
4944     safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
4945     p = buf;
4946     for (;;) {
4947         while(*p == ' ') p++;
4948         argv[i++] = p;
4949         if(*p == '"' || *p == '\'')
4950              p = strchr(++argv[i-1], *p);
4951         else p = strchr(p, ' ');
4952         if (p == NULL) break;
4953         *p++ = NULLCHAR;
4954     }
4955     argv[i] = NULL;
4956
4957     SetUpChildIO(to_prog, from_prog);
4958
4959     if ((pid = fork()) == 0) {
4960         /* Child process */
4961         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
4962         close(to_prog[1]);     // first close the unused pipe ends
4963         close(from_prog[0]);
4964         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
4965         dup2(from_prog[1], 1);
4966         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
4967         close(from_prog[1]);                   // and closing again loses one of the pipes!
4968         if(fileno(stderr) >= 2) // better safe than sorry...
4969                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
4970
4971         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
4972             perror(dir);
4973             exit(1);
4974         }
4975
4976         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
4977
4978         execvp(argv[0], argv);
4979
4980         /* If we get here, exec failed */
4981         perror(argv[0]);
4982         exit(1);
4983     }
4984
4985     /* Parent process */
4986     close(to_prog[0]);
4987     close(from_prog[1]);
4988
4989     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
4990     cp->kind = CPReal;
4991     cp->pid = pid;
4992     cp->fdFrom = from_prog[0];
4993     cp->fdTo = to_prog[1];
4994     *pr = (ProcRef) cp;
4995     return 0;
4996 }
4997
4998 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
4999 static RETSIGTYPE
5000 AlarmCallBack (int n)
5001 {
5002     return;
5003 }
5004
5005 void
5006 DestroyChildProcess (ProcRef pr, int signalType)
5007 {
5008     ChildProc *cp = (ChildProc *) pr;
5009
5010     if (cp->kind != CPReal) return;
5011     cp->kind = CPNone;
5012     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5013         signal(SIGALRM, AlarmCallBack);
5014         alarm(3);
5015         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5016             kill(cp->pid, SIGKILL); // kill it forcefully
5017             wait((int *) 0);        // and wait again
5018         }
5019     } else {
5020         if (signalType) {
5021             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5022         }
5023         /* Process is exiting either because of the kill or because of
5024            a quit command sent by the backend; either way, wait for it to die.
5025         */
5026         wait((int *) 0);
5027     }
5028     close(cp->fdFrom);
5029     close(cp->fdTo);
5030 }
5031
5032 void
5033 InterruptChildProcess (ProcRef pr)
5034 {
5035     ChildProc *cp = (ChildProc *) pr;
5036
5037     if (cp->kind != CPReal) return;
5038     (void) kill(cp->pid, SIGINT); /* stop it thinking */
5039 }
5040
5041 int
5042 OpenTelnet (char *host, char *port, ProcRef *pr)
5043 {
5044     char cmdLine[MSG_SIZ];
5045
5046     if (port[0] == NULLCHAR) {
5047       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5048     } else {
5049       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5050     }
5051     return StartChildProcess(cmdLine, "", pr);
5052 }
5053
5054 int
5055 OpenTCP (char *host, char *port, ProcRef *pr)
5056 {
5057 #if OMIT_SOCKETS
5058     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5059 #else  /* !OMIT_SOCKETS */
5060     struct addrinfo hints;
5061     struct addrinfo *ais, *ai;
5062     int error;
5063     int s=0;
5064     ChildProc *cp;
5065
5066     memset(&hints, 0, sizeof(hints));
5067     hints.ai_family = AF_UNSPEC;
5068     hints.ai_socktype = SOCK_STREAM;
5069
5070     error = getaddrinfo(host, port, &hints, &ais);
5071     if (error != 0) {
5072       /* a getaddrinfo error is not an errno, so can't return it */
5073       fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
5074               host, port, gai_strerror(error));
5075       return ENOENT;
5076     }
5077      
5078     for (ai = ais; ai != NULL; ai = ai->ai_next) {
5079       if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
5080         error = errno;
5081         continue;
5082       }
5083       if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
5084         error = errno;
5085         continue;
5086       }
5087       error = 0;
5088       break;
5089     }
5090     freeaddrinfo(ais);
5091
5092     if (error != 0) {
5093       return error;
5094     }
5095
5096     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5097     cp->kind = CPSock;
5098     cp->pid = 0;
5099     cp->fdFrom = s;
5100     cp->fdTo = s;
5101     *pr = (ProcRef) cp;
5102 #endif /* !OMIT_SOCKETS */
5103
5104     return 0;
5105 }
5106
5107 int
5108 OpenCommPort (char *name, ProcRef *pr)
5109 {
5110     int fd;
5111     ChildProc *cp;
5112
5113     fd = open(name, 2, 0);
5114     if (fd < 0) return errno;
5115
5116     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5117     cp->kind = CPComm;
5118     cp->pid = 0;
5119     cp->fdFrom = fd;
5120     cp->fdTo = fd;
5121     *pr = (ProcRef) cp;
5122
5123     return 0;
5124 }
5125
5126 int
5127 OpenLoopback (ProcRef *pr)
5128 {
5129     ChildProc *cp;
5130     int to[2], from[2];
5131
5132     SetUpChildIO(to, from);
5133
5134     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5135     cp->kind = CPLoop;
5136     cp->pid = 0;
5137     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
5138     cp->fdTo = to[1];
5139     *pr = (ProcRef) cp;
5140
5141     return 0;
5142 }
5143
5144 int
5145 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
5146 {
5147     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5148     return -1;
5149 }
5150
5151 #define INPUT_SOURCE_BUF_SIZE 8192
5152
5153 typedef struct {
5154     CPKind kind;
5155     int fd;
5156     int lineByLine;
5157     char *unused;
5158     InputCallback func;
5159     XtInputId xid;
5160     char buf[INPUT_SOURCE_BUF_SIZE];
5161     VOIDSTAR closure;
5162 } InputSource;
5163
5164 void
5165 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
5166 {
5167     InputSource *is = (InputSource *) closure;
5168     int count;
5169     int error;
5170     char *p, *q;
5171
5172     if (is->lineByLine) {
5173         count = read(is->fd, is->unused,
5174                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5175         if (count <= 0) {
5176             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5177             return;
5178         }
5179         is->unused += count;
5180         p = is->buf;
5181         while (p < is->unused) {
5182             q = memchr(p, '\n', is->unused - p);
5183             if (q == NULL) break;
5184             q++;
5185             (is->func)(is, is->closure, p, q - p, 0);
5186             p = q;
5187         }
5188         q = is->buf;
5189         while (p < is->unused) {
5190             *q++ = *p++;
5191         }
5192         is->unused = q;
5193     } else {
5194         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5195         if (count == -1)
5196           error = errno;
5197         else
5198           error = 0;
5199         (is->func)(is, is->closure, is->buf, count, error);
5200     }
5201 }
5202
5203 InputSourceRef
5204 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
5205 {
5206     InputSource *is;
5207     ChildProc *cp = (ChildProc *) pr;
5208
5209     is = (InputSource *) calloc(1, sizeof(InputSource));
5210     is->lineByLine = lineByLine;
5211     is->func = func;
5212     if (pr == NoProc) {
5213         is->kind = CPReal;
5214         is->fd = fileno(stdin);
5215     } else {
5216         is->kind = cp->kind;
5217         is->fd = cp->fdFrom;
5218     }
5219     if (lineByLine) {
5220         is->unused = is->buf;
5221     }
5222
5223     is->xid = XtAppAddInput(appContext, is->fd,
5224                             (XtPointer) (XtInputReadMask),
5225                             (XtInputCallbackProc) DoInputCallback,
5226                             (XtPointer) is);
5227     is->closure = closure;
5228     return (InputSourceRef) is;
5229 }
5230
5231 void
5232 RemoveInputSource (InputSourceRef isr)
5233 {
5234     InputSource *is = (InputSource *) isr;
5235
5236     if (is->xid == 0) return;
5237     XtRemoveInput(is->xid);
5238     is->xid = 0;
5239 }
5240
5241 int
5242 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
5243 {
5244     static int line = 0;
5245     ChildProc *cp = (ChildProc *) pr;
5246     int outCount;
5247
5248     if (pr == NoProc)
5249     {
5250         if (appData.noJoin || !appData.useInternalWrap)
5251             outCount = fwrite(message, 1, count, stdout);
5252         else
5253         {
5254             int width = get_term_width();
5255             int len = wrap(NULL, message, count, width, &line);
5256             char *msg = malloc(len);
5257             int dbgchk;
5258
5259             if (!msg)
5260                 outCount = fwrite(message, 1, count, stdout);
5261             else
5262             {
5263                 dbgchk = wrap(msg, message, count, width, &line);
5264                 if (dbgchk != len && appData.debugMode)
5265                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5266                 outCount = fwrite(msg, 1, dbgchk, stdout);
5267                 free(msg);
5268             }
5269         }
5270     }
5271     else
5272       outCount = write(cp->fdTo, message, count);
5273
5274     if (outCount == -1)
5275       *outError = errno;
5276     else
5277       *outError = 0;
5278
5279     return outCount;
5280 }
5281
5282 /* Output message to process, with "ms" milliseconds of delay
5283    between each character. This is needed when sending the logon
5284    script to ICC, which for some reason doesn't like the
5285    instantaneous send. */
5286 int
5287 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
5288 {
5289     ChildProc *cp = (ChildProc *) pr;
5290     int outCount = 0;
5291     int r;
5292
5293     while (count--) {
5294         r = write(cp->fdTo, message++, 1);
5295         if (r == -1) {
5296             *outError = errno;
5297             return outCount;
5298         }
5299         ++outCount;
5300         if (msdelay >= 0)
5301           TimeDelay(msdelay);
5302     }
5303
5304     return outCount;
5305 }
5306
5307 /****   Animation code by Hugh Fisher, DCS, ANU. ****/
5308
5309 /*      Masks for XPM pieces. Black and white pieces can have
5310         different shapes, but in the interest of retaining my
5311         sanity pieces must have the same outline on both light
5312         and dark squares, and all pieces must use the same
5313         background square colors/images.                */
5314
5315 static int xpmDone = 0;
5316 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
5317 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
5318
5319 static void
5320 CreateAnimMasks (int pieceDepth)
5321 {
5322   ChessSquare   piece;
5323   Pixmap        buf;
5324   GC            bufGC, maskGC;
5325   int           kind, n;
5326   unsigned long plane;
5327   XGCValues     values;
5328
5329   /* Need a bitmap just to get a GC with right depth */
5330   buf = XCreatePixmap(xDisplay, xBoardWindow,
5331                         8, 8, 1);
5332   values.foreground = 1;
5333   values.background = 0;
5334   /* Don't use XtGetGC, not read only */
5335   maskGC = XCreateGC(xDisplay, buf,
5336                     GCForeground | GCBackground, &values);
5337   XFreePixmap(xDisplay, buf);
5338
5339   buf = XCreatePixmap(xDisplay, xBoardWindow,
5340                       squareSize, squareSize, pieceDepth);
5341   values.foreground = XBlackPixel(xDisplay, xScreen);
5342   values.background = XWhitePixel(xDisplay, xScreen);
5343   bufGC = XCreateGC(xDisplay, buf,
5344                     GCForeground | GCBackground, &values);
5345
5346   for (piece = WhitePawn; piece <= BlackKing; piece++) {
5347     /* Begin with empty mask */
5348     if(!xpmDone) // [HGM] pieces: keep using existing
5349     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5350                                  squareSize, squareSize, 1);
5351     XSetFunction(xDisplay, maskGC, GXclear);
5352     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5353                    0, 0, squareSize, squareSize);
5354
5355     /* Take a copy of the piece */
5356     if (White(piece))
5357       kind = 0;
5358     else
5359       kind = 2;
5360     XSetFunction(xDisplay, bufGC, GXcopy);
5361     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5362               buf, bufGC,
5363               0, 0, squareSize, squareSize, 0, 0);
5364
5365     /* XOR the background (light) over the piece */
5366     XSetFunction(xDisplay, bufGC, GXxor);
5367     if (useImageSqs)
5368       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5369                 0, 0, squareSize, squareSize, 0, 0);
5370     else {
5371       XSetForeground(xDisplay, bufGC, lightSquareColor);
5372       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5373     }
5374
5375     /* We now have an inverted piece image with the background
5376        erased. Construct mask by just selecting all the non-zero
5377        pixels - no need to reconstruct the original image.      */
5378     XSetFunction(xDisplay, maskGC, GXor);
5379     plane = 1;
5380     /* Might be quicker to download an XImage and create bitmap
5381        data from it rather than this N copies per piece, but it
5382        only takes a fraction of a second and there is a much
5383        longer delay for loading the pieces.             */
5384     for (n = 0; n < pieceDepth; n ++) {
5385       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5386                  0, 0, squareSize, squareSize,
5387                  0, 0, plane);
5388       plane = plane << 1;
5389     }
5390   }
5391   /* Clean up */
5392   XFreePixmap(xDisplay, buf);
5393   XFreeGC(xDisplay, bufGC);
5394   XFreeGC(xDisplay, maskGC);
5395 }
5396
5397 static void
5398 InitAnimState (AnimNr anr, XWindowAttributes *info)
5399 {
5400   XtGCMask  mask;
5401   XGCValues values;
5402
5403   /* Each buffer is square size, same depth as window */
5404   animBufs[anr+4] = xBoardWindow;
5405   animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
5406                         squareSize, squareSize, info->depth);
5407   animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
5408                         squareSize, squareSize, info->depth);
5409
5410   /* Create a plain GC for blitting */
5411   mask = GCForeground | GCBackground | GCFunction |
5412          GCPlaneMask | GCGraphicsExposures;
5413   values.foreground = XBlackPixel(xDisplay, xScreen);
5414   values.background = XWhitePixel(xDisplay, xScreen);
5415   values.function   = GXcopy;
5416   values.plane_mask = AllPlanes;
5417   values.graphics_exposures = False;
5418   animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5419
5420   /* Piece will be copied from an existing context at
5421      the start of each new animation/drag. */
5422   animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5423
5424   /* Outline will be a read-only copy of an existing */
5425   animGCs[anr+4] = None;
5426 }
5427
5428 void
5429 CreateAnimVars ()
5430 {
5431   XWindowAttributes info;
5432
5433   if (xpmDone && gameInfo.variant == oldVariant) return;
5434   if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
5435   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5436
5437   InitAnimState(Game, &info);
5438   InitAnimState(Player, &info);
5439
5440   /* For XPM pieces, we need bitmaps to use as masks. */
5441   if (useImages)
5442     CreateAnimMasks(info.depth), xpmDone = 1;
5443 }
5444
5445 #ifndef HAVE_USLEEP
5446
5447 static Boolean frameWaiting;
5448
5449 static RETSIGTYPE
5450 FrameAlarm (int sig)
5451 {
5452   frameWaiting = False;
5453   /* In case System-V style signals.  Needed?? */
5454   signal(SIGALRM, FrameAlarm);
5455 }
5456
5457 void
5458 FrameDelay (int time)
5459 {
5460   struct itimerval delay;
5461
5462   XSync(xDisplay, False);
5463
5464   if (time > 0) {
5465     frameWaiting = True;
5466     signal(SIGALRM, FrameAlarm);
5467     delay.it_interval.tv_sec =
5468       delay.it_value.tv_sec = time / 1000;
5469     delay.it_interval.tv_usec =
5470       delay.it_value.tv_usec = (time % 1000) * 1000;
5471     setitimer(ITIMER_REAL, &delay, NULL);
5472     while (frameWaiting) pause();
5473     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5474     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5475     setitimer(ITIMER_REAL, &delay, NULL);
5476   }
5477 }
5478
5479 #else
5480
5481 void
5482 FrameDelay (int time)
5483 {
5484   XSync(xDisplay, False);
5485   if (time > 0)
5486     usleep(time * 1000);
5487 }
5488
5489 #endif
5490
5491 static void
5492 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
5493 {
5494   GC source;
5495
5496   /* Bitmap for piece being moved. */
5497   if (appData.monoMode) {
5498       *mask = *pieceToSolid(piece);
5499   } else if (useImages) {
5500 #if HAVE_LIBXPM
5501       *mask = xpmMask[piece];
5502 #else
5503       *mask = ximMaskPm[piece];
5504 #endif
5505   } else {
5506       *mask = *pieceToSolid(piece);
5507   }
5508
5509   /* GC for piece being moved. Square color doesn't matter, but
5510      since it gets modified we make a copy of the original. */
5511   if (White(piece)) {
5512     if (appData.monoMode)
5513       source = bwPieceGC;
5514     else
5515       source = wlPieceGC;
5516   } else {
5517     if (appData.monoMode)
5518       source = wbPieceGC;
5519     else
5520       source = blPieceGC;
5521   }
5522   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5523
5524   /* Outline only used in mono mode and is not modified */
5525   if (White(piece))
5526     *outline = bwPieceGC;
5527   else
5528     *outline = wbPieceGC;
5529 }
5530
5531 static void
5532 OverlayPiece (ChessSquare piece, GC clip, GC outline,  Drawable dest)
5533 {
5534   int   kind;
5535
5536   if (!useImages) {
5537     /* Draw solid rectangle which will be clipped to shape of piece */
5538     XFillRectangle(xDisplay, dest, clip,
5539                    0, 0, squareSize, squareSize);
5540     if (appData.monoMode)
5541       /* Also draw outline in contrasting color for black
5542          on black / white on white cases                */
5543       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5544                  0, 0, squareSize, squareSize, 0, 0, 1);
5545   } else {
5546     /* Copy the piece */
5547     if (White(piece))
5548       kind = 0;
5549     else
5550       kind = 2;
5551     if(appData.upsideDown && flipView) kind ^= 2;
5552     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5553               dest, clip,
5554               0, 0, squareSize, squareSize,
5555               0, 0);
5556   }
5557 }
5558
5559 void
5560 InsertPiece (AnimNr anr, ChessSquare piece)
5561 {
5562   OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
5563 }
5564
5565 void
5566 DrawBlank (AnimNr anr, int x, int y, int startColor)
5567 {
5568     BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
5569 }
5570
5571 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
5572                  int srcX, int srcY, int width, int height, int destX, int destY)
5573 {
5574     XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
5575                 srcX, srcY, width, height, destX, destY);
5576 }
5577
5578 void
5579 SetDragPiece (AnimNr anr, ChessSquare piece)
5580 {
5581   Pixmap mask;
5582   /* The piece will be drawn using its own bitmap as a matte    */
5583   SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
5584   XSetClipMask(xDisplay, animGCs[anr+2], mask);
5585 }
5586
5587 #include <sys/ioctl.h>
5588 int
5589 get_term_width ()
5590 {
5591     int fd, default_width;
5592
5593     fd = STDIN_FILENO;
5594     default_width = 79; // this is FICS default anyway...
5595
5596 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
5597     struct ttysize win;
5598     if (!ioctl(fd, TIOCGSIZE, &win))
5599         default_width = win.ts_cols;
5600 #elif defined(TIOCGWINSZ)
5601     struct winsize win;
5602     if (!ioctl(fd, TIOCGWINSZ, &win))
5603         default_width = win.ws_col;
5604 #endif
5605     return default_width;
5606 }
5607
5608 void
5609 update_ics_width ()
5610 {
5611   static int old_width = 0;
5612   int new_width = get_term_width();
5613
5614   if (old_width != new_width)
5615     ics_printf("set width %d\n", new_width);
5616   old_width = new_width;
5617 }
5618
5619 void
5620 NotifyFrontendLogin ()
5621 {
5622     update_ics_width();
5623 }
5624
5625 /* [AS] Arrow highlighting support */
5626
5627 void
5628 DrawPolygon (Pnt arrow[], int nr)
5629 {
5630     XPoint pts[10];
5631     int i;
5632     for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
5633     XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
5634     if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
5635 }
5636
5637 void
5638 UpdateLogos (int displ)
5639 {
5640     return; // no logos in XBoard yet
5641 }
5642