Remove one level of indirection on ICSInputBoxPopUp
[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 static void
4237 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
4238 {
4239     char buf[10], keys[32];
4240     KeySym sym;
4241     KeyCode metaL, metaR; //, ctrlL, ctrlR;
4242     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
4243     XQueryKeymap(xDisplay,keys);
4244     metaL = XKeysymToKeycode(xDisplay, XK_Meta_L);
4245     metaR = XKeysymToKeycode(xDisplay, XK_Meta_R);
4246 //    ctrlL = XKeysymToKeycode(xDisplay, XK_Control_L);
4247 //    ctrlR = XKeysymToKeycode(xDisplay, XK_Control_R);
4248     if ( n == 1 && *buf >= 32 // printable
4249          && !(keys[metaL>>3]&1<<(metaL&7)) && !(keys[metaR>>3]&1<<(metaR&7)) // no alt key pressed
4250 //       && !(keys[ctrlL>>3]&1<<(ctrlL&7)) && !(keys[ctrlR>>3]&1<<(ctrlR&7)) // no ctrl key pressed
4251        ) BoxAutoPopUp (buf);
4252 }
4253
4254 static void
4255 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4256 {   // [HGM] input: let up-arrow recall previous line from history
4257     IcsKey(1);
4258 }
4259
4260 static void
4261 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4262 {   // [HGM] input: let down-arrow recall next line from history
4263     IcsKey(-1);
4264 }
4265
4266 static void
4267 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4268 {
4269     IcsKey(0);
4270 }
4271
4272 void
4273 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4274 {
4275         if (!TempBackwardActive) {
4276                 TempBackwardActive = True;
4277                 BackwardEvent();
4278         }
4279 }
4280
4281 void
4282 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4283 {
4284         /* Check to see if triggered by a key release event for a repeating key.
4285          * If so the next queued event will be a key press of the same key at the same time */
4286         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
4287                 XEvent next;
4288                 XPeekEvent(xDisplay, &next);
4289                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
4290                         next.xkey.keycode == event->xkey.keycode)
4291                                 return;
4292         }
4293     ForwardEvent();
4294         TempBackwardActive = False;
4295 }
4296
4297 void
4298 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4299 {   // called as key binding
4300     char buf[MSG_SIZ];
4301     String name;
4302     if (nprms && *nprms > 0)
4303       name = prms[0];
4304     else
4305       name = "xboard";
4306     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
4307     system(buf);
4308 }
4309
4310 void
4311 DisplayMessage (char *message, char *extMessage)
4312 {
4313   /* display a message in the message widget */
4314
4315   char buf[MSG_SIZ];
4316   Arg arg;
4317
4318   if (extMessage)
4319     {
4320       if (*message)
4321         {
4322           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
4323           message = buf;
4324         }
4325       else
4326         {
4327           message = extMessage;
4328         };
4329     };
4330
4331     safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
4332
4333   /* need to test if messageWidget already exists, since this function
4334      can also be called during the startup, if for example a Xresource
4335      is not set up correctly */
4336   if(messageWidget)
4337     {
4338       XtSetArg(arg, XtNlabel, message);
4339       XtSetValues(messageWidget, &arg, 1);
4340     };
4341
4342   return;
4343 }
4344
4345 void
4346 SetWindowTitle (char *text, char *title, char *icon)
4347 {
4348     Arg args[16];
4349     int i;
4350     if (appData.titleInWindow) {
4351         i = 0;
4352         XtSetArg(args[i], XtNlabel, text);   i++;
4353         XtSetValues(titleWidget, args, i);
4354     }
4355     i = 0;
4356     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
4357     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
4358     XtSetValues(shellWidget, args, i);
4359     XSync(xDisplay, False);
4360 }
4361
4362
4363 static int
4364 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
4365 {
4366     return 0;
4367 }
4368
4369 void
4370 DisplayIcsInteractionTitle (String message)
4371 {
4372   if (oldICSInteractionTitle == NULL) {
4373     /* Magic to find the old window title, adapted from vim */
4374     char *wina = getenv("WINDOWID");
4375     if (wina != NULL) {
4376       Window win = (Window) atoi(wina);
4377       Window root, parent, *children;
4378       unsigned int nchildren;
4379       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4380       for (;;) {
4381         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4382         if (!XQueryTree(xDisplay, win, &root, &parent,
4383                         &children, &nchildren)) break;
4384         if (children) XFree((void *)children);
4385         if (parent == root || parent == 0) break;
4386         win = parent;
4387       }
4388       XSetErrorHandler(oldHandler);
4389     }
4390     if (oldICSInteractionTitle == NULL) {
4391       oldICSInteractionTitle = "xterm";
4392     }
4393   }
4394   printf("\033]0;%s\007", message);
4395   fflush(stdout);
4396 }
4397
4398 char pendingReplyPrefix[MSG_SIZ];
4399 ProcRef pendingReplyPR;
4400
4401 void
4402 AskQuestionPopDown ()
4403 {
4404     if (!askQuestionUp) return;
4405     XtPopdown(askQuestionShell);
4406     XtDestroyWidget(askQuestionShell);
4407     askQuestionUp = False;
4408 }
4409
4410 void
4411 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4412 {
4413     char buf[MSG_SIZ];
4414     int err;
4415     String reply;
4416
4417     reply = XawDialogGetValueString(w = XtParent(w));
4418     safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
4419     if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
4420     strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
4421     strncat(buf, "\n",  MSG_SIZ - strlen(buf) - 1);
4422     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4423     AskQuestionPopDown();
4424
4425     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4426 }
4427
4428 void
4429 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4430 {
4431     String name;
4432     Arg args[16];
4433
4434     XtSetArg(args[0], XtNlabel, &name);
4435     XtGetValues(w, args, 1);
4436
4437     if (strcmp(name, _("cancel")) == 0) {
4438         AskQuestionPopDown();
4439     } else {
4440         AskQuestionReplyAction(w, NULL, NULL, NULL);
4441     }
4442 }
4443
4444 void
4445 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
4446 {
4447     Arg args[16];
4448     Widget popup, layout, dialog, edit;
4449     Window root, child;
4450     int x, y, i;
4451     int win_x, win_y;
4452     unsigned int mask;
4453
4454     safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
4455     pendingReplyPR = pr;
4456
4457     i = 0;
4458     XtSetArg(args[i], XtNresizable, True); i++;
4459     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4460     askQuestionShell = popup =
4461       XtCreatePopupShell(title, transientShellWidgetClass,
4462                          shellWidget, args, i);
4463
4464     layout =
4465       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4466                             layoutArgs, XtNumber(layoutArgs));
4467
4468     i = 0;
4469     XtSetArg(args[i], XtNlabel, question); i++;
4470     XtSetArg(args[i], XtNvalue, ""); i++;
4471     XtSetArg(args[i], XtNborderWidth, 0); i++;
4472     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4473                                    layout, args, i);
4474
4475     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4476                        (XtPointer) dialog);
4477     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4478                        (XtPointer) dialog);
4479
4480     XtRealizeWidget(popup);
4481     CatchDeleteWindow(popup, "AskQuestionPopDown");
4482
4483     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4484                   &x, &y, &win_x, &win_y, &mask);
4485
4486     XtSetArg(args[0], XtNx, x - 10);
4487     XtSetArg(args[1], XtNy, y - 30);
4488     XtSetValues(popup, args, 2);
4489
4490     XtPopup(popup, XtGrabExclusive);
4491     askQuestionUp = True;
4492
4493     edit = XtNameToWidget(dialog, "*value");
4494     XtSetKeyboardFocus(popup, edit);
4495 }
4496
4497
4498 void
4499 PlaySound (char *name)
4500 {
4501   if (*name == NULLCHAR) {
4502     return;
4503   } else if (strcmp(name, "$") == 0) {
4504     putc(BELLCHAR, stderr);
4505   } else {
4506     char buf[2048];
4507     char *prefix = "", *sep = "";
4508     if(appData.soundProgram[0] == NULLCHAR) return;
4509     if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
4510     snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
4511     system(buf);
4512   }
4513 }
4514
4515 void
4516 RingBell ()
4517 {
4518   PlaySound(appData.soundMove);
4519 }
4520
4521 void
4522 PlayIcsWinSound ()
4523 {
4524   PlaySound(appData.soundIcsWin);
4525 }
4526
4527 void
4528 PlayIcsLossSound ()
4529 {
4530   PlaySound(appData.soundIcsLoss);
4531 }
4532
4533 void
4534 PlayIcsDrawSound ()
4535 {
4536   PlaySound(appData.soundIcsDraw);
4537 }
4538
4539 void
4540 PlayIcsUnfinishedSound ()
4541 {
4542   PlaySound(appData.soundIcsUnfinished);
4543 }
4544
4545 void
4546 PlayAlarmSound ()
4547 {
4548   PlaySound(appData.soundIcsAlarm);
4549 }
4550
4551 void
4552 PlayTellSound ()
4553 {
4554   PlaySound(appData.soundTell);
4555 }
4556
4557 void
4558 EchoOn ()
4559 {
4560     system("stty echo");
4561     noEcho = False;
4562 }
4563
4564 void
4565 EchoOff ()
4566 {
4567     system("stty -echo");
4568     noEcho = True;
4569 }
4570
4571 void
4572 RunCommand (char *buf)
4573 {
4574     system(buf);
4575 }
4576
4577 void
4578 Colorize (ColorClass cc, int continuation)
4579 {
4580     char buf[MSG_SIZ];
4581     int count, outCount, error;
4582
4583     if (textColors[(int)cc].bg > 0) {
4584         if (textColors[(int)cc].fg > 0) {
4585           snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4586                    textColors[(int)cc].fg, textColors[(int)cc].bg);
4587         } else {
4588           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4589                    textColors[(int)cc].bg);
4590         }
4591     } else {
4592         if (textColors[(int)cc].fg > 0) {
4593           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4594                     textColors[(int)cc].fg);
4595         } else {
4596           snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
4597         }
4598     }
4599     count = strlen(buf);
4600     outCount = OutputToProcess(NoProc, buf, count, &error);
4601     if (outCount < count) {
4602         DisplayFatalError(_("Error writing to display"), error, 1);
4603     }
4604
4605     if (continuation) return;
4606     switch (cc) {
4607     case ColorShout:
4608       PlaySound(appData.soundShout);
4609       break;
4610     case ColorSShout:
4611       PlaySound(appData.soundSShout);
4612       break;
4613     case ColorChannel1:
4614       PlaySound(appData.soundChannel1);
4615       break;
4616     case ColorChannel:
4617       PlaySound(appData.soundChannel);
4618       break;
4619     case ColorKibitz:
4620       PlaySound(appData.soundKibitz);
4621       break;
4622     case ColorTell:
4623       PlaySound(appData.soundTell);
4624       break;
4625     case ColorChallenge:
4626       PlaySound(appData.soundChallenge);
4627       break;
4628     case ColorRequest:
4629       PlaySound(appData.soundRequest);
4630       break;
4631     case ColorSeek:
4632       PlaySound(appData.soundSeek);
4633       break;
4634     case ColorNormal:
4635     case ColorNone:
4636     default:
4637       break;
4638     }
4639 }
4640
4641 char *
4642 UserName ()
4643 {
4644     return getpwuid(getuid())->pw_name;
4645 }
4646
4647 static char *
4648 ExpandPathName (char *path)
4649 {
4650     static char static_buf[4*MSG_SIZ];
4651     char *d, *s, buf[4*MSG_SIZ];
4652     struct passwd *pwd;
4653
4654     s = path;
4655     d = static_buf;
4656
4657     while (*s && isspace(*s))
4658       ++s;
4659
4660     if (!*s) {
4661         *d = 0;
4662         return static_buf;
4663     }
4664
4665     if (*s == '~') {
4666         if (*(s+1) == '/') {
4667           safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
4668           strcat(d, s+1);
4669         }
4670         else {
4671           safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
4672           { char *p; if(p = strchr(buf, '/')) *p = 0; }
4673           pwd = getpwnam(buf);
4674           if (!pwd)
4675             {
4676               fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
4677                       buf, path);
4678               return NULL;
4679             }
4680           safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
4681           strcat(d, strchr(s+1, '/'));
4682         }
4683     }
4684     else
4685       safeStrCpy(d, s, 4*MSG_SIZ );
4686
4687     return static_buf;
4688 }
4689
4690 char *
4691 HostName ()
4692 {
4693     static char host_name[MSG_SIZ];
4694
4695 #if HAVE_GETHOSTNAME
4696     gethostname(host_name, MSG_SIZ);
4697     return host_name;
4698 #else  /* not HAVE_GETHOSTNAME */
4699 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
4700     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
4701     return host_name;
4702 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4703     return "localhost";
4704 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4705 #endif /* not HAVE_GETHOSTNAME */
4706 }
4707
4708 XtIntervalId delayedEventTimerXID = 0;
4709 DelayedEventCallback delayedEventCallback = 0;
4710
4711 void
4712 FireDelayedEvent ()
4713 {
4714     delayedEventTimerXID = 0;
4715     delayedEventCallback();
4716 }
4717
4718 void
4719 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
4720 {
4721     if(delayedEventTimerXID && delayedEventCallback == cb)
4722         // [HGM] alive: replace, rather than add or flush identical event
4723         XtRemoveTimeOut(delayedEventTimerXID);
4724     delayedEventCallback = cb;
4725     delayedEventTimerXID =
4726       XtAppAddTimeOut(appContext, millisec,
4727                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
4728 }
4729
4730 DelayedEventCallback
4731 GetDelayedEvent ()
4732 {
4733   if (delayedEventTimerXID) {
4734     return delayedEventCallback;
4735   } else {
4736     return NULL;
4737   }
4738 }
4739
4740 void
4741 CancelDelayedEvent ()
4742 {
4743   if (delayedEventTimerXID) {
4744     XtRemoveTimeOut(delayedEventTimerXID);
4745     delayedEventTimerXID = 0;
4746   }
4747 }
4748
4749 XtIntervalId loadGameTimerXID = 0;
4750
4751 int
4752 LoadGameTimerRunning ()
4753 {
4754     return loadGameTimerXID != 0;
4755 }
4756
4757 int
4758 StopLoadGameTimer ()
4759 {
4760     if (loadGameTimerXID != 0) {
4761         XtRemoveTimeOut(loadGameTimerXID);
4762         loadGameTimerXID = 0;
4763         return TRUE;
4764     } else {
4765         return FALSE;
4766     }
4767 }
4768
4769 void
4770 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
4771 {
4772     loadGameTimerXID = 0;
4773     AutoPlayGameLoop();
4774 }
4775
4776 void
4777 StartLoadGameTimer (long millisec)
4778 {
4779     loadGameTimerXID =
4780       XtAppAddTimeOut(appContext, millisec,
4781                       (XtTimerCallbackProc) LoadGameTimerCallback,
4782                       (XtPointer) 0);
4783 }
4784
4785 XtIntervalId analysisClockXID = 0;
4786
4787 void
4788 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
4789 {
4790     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4791          || appData.icsEngineAnalyze) { // [DM]
4792         AnalysisPeriodicEvent(0);
4793         StartAnalysisClock();
4794     }
4795 }
4796
4797 void
4798 StartAnalysisClock ()
4799 {
4800     analysisClockXID =
4801       XtAppAddTimeOut(appContext, 2000,
4802                       (XtTimerCallbackProc) AnalysisClockCallback,
4803                       (XtPointer) 0);
4804 }
4805
4806 XtIntervalId clockTimerXID = 0;
4807
4808 int
4809 ClockTimerRunning ()
4810 {
4811     return clockTimerXID != 0;
4812 }
4813
4814 int
4815 StopClockTimer ()
4816 {
4817     if (clockTimerXID != 0) {
4818         XtRemoveTimeOut(clockTimerXID);
4819         clockTimerXID = 0;
4820         return TRUE;
4821     } else {
4822         return FALSE;
4823     }
4824 }
4825
4826 void
4827 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
4828 {
4829     clockTimerXID = 0;
4830     DecrementClocks();
4831 }
4832
4833 void
4834 StartClockTimer (long millisec)
4835 {
4836     clockTimerXID =
4837       XtAppAddTimeOut(appContext, millisec,
4838                       (XtTimerCallbackProc) ClockTimerCallback,
4839                       (XtPointer) 0);
4840 }
4841
4842 void
4843 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
4844 {
4845     char buf[MSG_SIZ];
4846     Arg args[16];
4847
4848     /* check for low time warning */
4849     Pixel foregroundOrWarningColor = timerForegroundPixel;
4850
4851     if (timer > 0 &&
4852         appData.lowTimeWarning &&
4853         (timer / 1000) < appData.icsAlarmTime)
4854       foregroundOrWarningColor = lowTimeWarningColor;
4855
4856     if (appData.clockMode) {
4857       snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
4858       XtSetArg(args[0], XtNlabel, buf);
4859     } else {
4860       snprintf(buf, MSG_SIZ, "%s  ", color);
4861       XtSetArg(args[0], XtNlabel, buf);
4862     }
4863
4864     if (highlight) {
4865
4866         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
4867         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
4868     } else {
4869         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
4870         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
4871     }
4872
4873     XtSetValues(w, args, 3);
4874 }
4875
4876 void
4877 DisplayWhiteClock (long timeRemaining, int highlight)
4878 {
4879     Arg args[16];
4880
4881     if(appData.noGUI) return;
4882     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
4883     if (highlight && iconPixmap == bIconPixmap) {
4884         iconPixmap = wIconPixmap;
4885         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4886         XtSetValues(shellWidget, args, 1);
4887     }
4888 }
4889
4890 void
4891 DisplayBlackClock (long timeRemaining, int highlight)
4892 {
4893     Arg args[16];
4894
4895     if(appData.noGUI) return;
4896     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
4897     if (highlight && iconPixmap == wIconPixmap) {
4898         iconPixmap = bIconPixmap;
4899         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4900         XtSetValues(shellWidget, args, 1);
4901     }
4902 }
4903
4904 #define CPNone 0
4905 #define CPReal 1
4906 #define CPComm 2
4907 #define CPSock 3
4908 #define CPLoop 4
4909 typedef int CPKind;
4910
4911 typedef struct {
4912     CPKind kind;
4913     int pid;
4914     int fdTo, fdFrom;
4915 } ChildProc;
4916
4917
4918 int
4919 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
4920 {
4921     char *argv[64], *p;
4922     int i, pid;
4923     int to_prog[2], from_prog[2];
4924     ChildProc *cp;
4925     char buf[MSG_SIZ];
4926
4927     if (appData.debugMode) {
4928         fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
4929     }
4930
4931     /* We do NOT feed the cmdLine to the shell; we just
4932        parse it into blank-separated arguments in the
4933        most simple-minded way possible.
4934        */
4935     i = 0;
4936     safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
4937     p = buf;
4938     for (;;) {
4939         while(*p == ' ') p++;
4940         argv[i++] = p;
4941         if(*p == '"' || *p == '\'')
4942              p = strchr(++argv[i-1], *p);
4943         else p = strchr(p, ' ');
4944         if (p == NULL) break;
4945         *p++ = NULLCHAR;
4946     }
4947     argv[i] = NULL;
4948
4949     SetUpChildIO(to_prog, from_prog);
4950
4951     if ((pid = fork()) == 0) {
4952         /* Child process */
4953         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
4954         close(to_prog[1]);     // first close the unused pipe ends
4955         close(from_prog[0]);
4956         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
4957         dup2(from_prog[1], 1);
4958         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
4959         close(from_prog[1]);                   // and closing again loses one of the pipes!
4960         if(fileno(stderr) >= 2) // better safe than sorry...
4961                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
4962
4963         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
4964             perror(dir);
4965             exit(1);
4966         }
4967
4968         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
4969
4970         execvp(argv[0], argv);
4971
4972         /* If we get here, exec failed */
4973         perror(argv[0]);
4974         exit(1);
4975     }
4976
4977     /* Parent process */
4978     close(to_prog[0]);
4979     close(from_prog[1]);
4980
4981     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
4982     cp->kind = CPReal;
4983     cp->pid = pid;
4984     cp->fdFrom = from_prog[0];
4985     cp->fdTo = to_prog[1];
4986     *pr = (ProcRef) cp;
4987     return 0;
4988 }
4989
4990 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
4991 static RETSIGTYPE
4992 AlarmCallBack (int n)
4993 {
4994     return;
4995 }
4996
4997 void
4998 DestroyChildProcess (ProcRef pr, int signalType)
4999 {
5000     ChildProc *cp = (ChildProc *) pr;
5001
5002     if (cp->kind != CPReal) return;
5003     cp->kind = CPNone;
5004     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5005         signal(SIGALRM, AlarmCallBack);
5006         alarm(3);
5007         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5008             kill(cp->pid, SIGKILL); // kill it forcefully
5009             wait((int *) 0);        // and wait again
5010         }
5011     } else {
5012         if (signalType) {
5013             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5014         }
5015         /* Process is exiting either because of the kill or because of
5016            a quit command sent by the backend; either way, wait for it to die.
5017         */
5018         wait((int *) 0);
5019     }
5020     close(cp->fdFrom);
5021     close(cp->fdTo);
5022 }
5023
5024 void
5025 InterruptChildProcess (ProcRef pr)
5026 {
5027     ChildProc *cp = (ChildProc *) pr;
5028
5029     if (cp->kind != CPReal) return;
5030     (void) kill(cp->pid, SIGINT); /* stop it thinking */
5031 }
5032
5033 int
5034 OpenTelnet (char *host, char *port, ProcRef *pr)
5035 {
5036     char cmdLine[MSG_SIZ];
5037
5038     if (port[0] == NULLCHAR) {
5039       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5040     } else {
5041       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5042     }
5043     return StartChildProcess(cmdLine, "", pr);
5044 }
5045
5046 int
5047 OpenTCP (char *host, char *port, ProcRef *pr)
5048 {
5049 #if OMIT_SOCKETS
5050     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5051 #else  /* !OMIT_SOCKETS */
5052     struct addrinfo hints;
5053     struct addrinfo *ais, *ai;
5054     int error;
5055     int s=0;
5056     ChildProc *cp;
5057
5058     memset(&hints, 0, sizeof(hints));
5059     hints.ai_family = AF_UNSPEC;
5060     hints.ai_socktype = SOCK_STREAM;
5061
5062     error = getaddrinfo(host, port, &hints, &ais);
5063     if (error != 0) {
5064       /* a getaddrinfo error is not an errno, so can't return it */
5065       fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
5066               host, port, gai_strerror(error));
5067       return ENOENT;
5068     }
5069      
5070     for (ai = ais; ai != NULL; ai = ai->ai_next) {
5071       if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
5072         error = errno;
5073         continue;
5074       }
5075       if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
5076         error = errno;
5077         continue;
5078       }
5079       error = 0;
5080       break;
5081     }
5082     freeaddrinfo(ais);
5083
5084     if (error != 0) {
5085       return error;
5086     }
5087
5088     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5089     cp->kind = CPSock;
5090     cp->pid = 0;
5091     cp->fdFrom = s;
5092     cp->fdTo = s;
5093     *pr = (ProcRef) cp;
5094 #endif /* !OMIT_SOCKETS */
5095
5096     return 0;
5097 }
5098
5099 int
5100 OpenCommPort (char *name, ProcRef *pr)
5101 {
5102     int fd;
5103     ChildProc *cp;
5104
5105     fd = open(name, 2, 0);
5106     if (fd < 0) return errno;
5107
5108     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5109     cp->kind = CPComm;
5110     cp->pid = 0;
5111     cp->fdFrom = fd;
5112     cp->fdTo = fd;
5113     *pr = (ProcRef) cp;
5114
5115     return 0;
5116 }
5117
5118 int
5119 OpenLoopback (ProcRef *pr)
5120 {
5121     ChildProc *cp;
5122     int to[2], from[2];
5123
5124     SetUpChildIO(to, from);
5125
5126     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5127     cp->kind = CPLoop;
5128     cp->pid = 0;
5129     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
5130     cp->fdTo = to[1];
5131     *pr = (ProcRef) cp;
5132
5133     return 0;
5134 }
5135
5136 int
5137 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
5138 {
5139     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5140     return -1;
5141 }
5142
5143 #define INPUT_SOURCE_BUF_SIZE 8192
5144
5145 typedef struct {
5146     CPKind kind;
5147     int fd;
5148     int lineByLine;
5149     char *unused;
5150     InputCallback func;
5151     XtInputId xid;
5152     char buf[INPUT_SOURCE_BUF_SIZE];
5153     VOIDSTAR closure;
5154 } InputSource;
5155
5156 void
5157 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
5158 {
5159     InputSource *is = (InputSource *) closure;
5160     int count;
5161     int error;
5162     char *p, *q;
5163
5164     if (is->lineByLine) {
5165         count = read(is->fd, is->unused,
5166                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5167         if (count <= 0) {
5168             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5169             return;
5170         }
5171         is->unused += count;
5172         p = is->buf;
5173         while (p < is->unused) {
5174             q = memchr(p, '\n', is->unused - p);
5175             if (q == NULL) break;
5176             q++;
5177             (is->func)(is, is->closure, p, q - p, 0);
5178             p = q;
5179         }
5180         q = is->buf;
5181         while (p < is->unused) {
5182             *q++ = *p++;
5183         }
5184         is->unused = q;
5185     } else {
5186         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5187         if (count == -1)
5188           error = errno;
5189         else
5190           error = 0;
5191         (is->func)(is, is->closure, is->buf, count, error);
5192     }
5193 }
5194
5195 InputSourceRef
5196 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
5197 {
5198     InputSource *is;
5199     ChildProc *cp = (ChildProc *) pr;
5200
5201     is = (InputSource *) calloc(1, sizeof(InputSource));
5202     is->lineByLine = lineByLine;
5203     is->func = func;
5204     if (pr == NoProc) {
5205         is->kind = CPReal;
5206         is->fd = fileno(stdin);
5207     } else {
5208         is->kind = cp->kind;
5209         is->fd = cp->fdFrom;
5210     }
5211     if (lineByLine) {
5212         is->unused = is->buf;
5213     }
5214
5215     is->xid = XtAppAddInput(appContext, is->fd,
5216                             (XtPointer) (XtInputReadMask),
5217                             (XtInputCallbackProc) DoInputCallback,
5218                             (XtPointer) is);
5219     is->closure = closure;
5220     return (InputSourceRef) is;
5221 }
5222
5223 void
5224 RemoveInputSource (InputSourceRef isr)
5225 {
5226     InputSource *is = (InputSource *) isr;
5227
5228     if (is->xid == 0) return;
5229     XtRemoveInput(is->xid);
5230     is->xid = 0;
5231 }
5232
5233 int
5234 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
5235 {
5236     static int line = 0;
5237     ChildProc *cp = (ChildProc *) pr;
5238     int outCount;
5239
5240     if (pr == NoProc)
5241     {
5242         if (appData.noJoin || !appData.useInternalWrap)
5243             outCount = fwrite(message, 1, count, stdout);
5244         else
5245         {
5246             int width = get_term_width();
5247             int len = wrap(NULL, message, count, width, &line);
5248             char *msg = malloc(len);
5249             int dbgchk;
5250
5251             if (!msg)
5252                 outCount = fwrite(message, 1, count, stdout);
5253             else
5254             {
5255                 dbgchk = wrap(msg, message, count, width, &line);
5256                 if (dbgchk != len && appData.debugMode)
5257                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5258                 outCount = fwrite(msg, 1, dbgchk, stdout);
5259                 free(msg);
5260             }
5261         }
5262     }
5263     else
5264       outCount = write(cp->fdTo, message, count);
5265
5266     if (outCount == -1)
5267       *outError = errno;
5268     else
5269       *outError = 0;
5270
5271     return outCount;
5272 }
5273
5274 /* Output message to process, with "ms" milliseconds of delay
5275    between each character. This is needed when sending the logon
5276    script to ICC, which for some reason doesn't like the
5277    instantaneous send. */
5278 int
5279 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
5280 {
5281     ChildProc *cp = (ChildProc *) pr;
5282     int outCount = 0;
5283     int r;
5284
5285     while (count--) {
5286         r = write(cp->fdTo, message++, 1);
5287         if (r == -1) {
5288             *outError = errno;
5289             return outCount;
5290         }
5291         ++outCount;
5292         if (msdelay >= 0)
5293           TimeDelay(msdelay);
5294     }
5295
5296     return outCount;
5297 }
5298
5299 /****   Animation code by Hugh Fisher, DCS, ANU. ****/
5300
5301 /*      Masks for XPM pieces. Black and white pieces can have
5302         different shapes, but in the interest of retaining my
5303         sanity pieces must have the same outline on both light
5304         and dark squares, and all pieces must use the same
5305         background square colors/images.                */
5306
5307 static int xpmDone = 0;
5308 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
5309 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
5310
5311 static void
5312 CreateAnimMasks (int pieceDepth)
5313 {
5314   ChessSquare   piece;
5315   Pixmap        buf;
5316   GC            bufGC, maskGC;
5317   int           kind, n;
5318   unsigned long plane;
5319   XGCValues     values;
5320
5321   /* Need a bitmap just to get a GC with right depth */
5322   buf = XCreatePixmap(xDisplay, xBoardWindow,
5323                         8, 8, 1);
5324   values.foreground = 1;
5325   values.background = 0;
5326   /* Don't use XtGetGC, not read only */
5327   maskGC = XCreateGC(xDisplay, buf,
5328                     GCForeground | GCBackground, &values);
5329   XFreePixmap(xDisplay, buf);
5330
5331   buf = XCreatePixmap(xDisplay, xBoardWindow,
5332                       squareSize, squareSize, pieceDepth);
5333   values.foreground = XBlackPixel(xDisplay, xScreen);
5334   values.background = XWhitePixel(xDisplay, xScreen);
5335   bufGC = XCreateGC(xDisplay, buf,
5336                     GCForeground | GCBackground, &values);
5337
5338   for (piece = WhitePawn; piece <= BlackKing; piece++) {
5339     /* Begin with empty mask */
5340     if(!xpmDone) // [HGM] pieces: keep using existing
5341     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5342                                  squareSize, squareSize, 1);
5343     XSetFunction(xDisplay, maskGC, GXclear);
5344     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5345                    0, 0, squareSize, squareSize);
5346
5347     /* Take a copy of the piece */
5348     if (White(piece))
5349       kind = 0;
5350     else
5351       kind = 2;
5352     XSetFunction(xDisplay, bufGC, GXcopy);
5353     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5354               buf, bufGC,
5355               0, 0, squareSize, squareSize, 0, 0);
5356
5357     /* XOR the background (light) over the piece */
5358     XSetFunction(xDisplay, bufGC, GXxor);
5359     if (useImageSqs)
5360       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5361                 0, 0, squareSize, squareSize, 0, 0);
5362     else {
5363       XSetForeground(xDisplay, bufGC, lightSquareColor);
5364       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5365     }
5366
5367     /* We now have an inverted piece image with the background
5368        erased. Construct mask by just selecting all the non-zero
5369        pixels - no need to reconstruct the original image.      */
5370     XSetFunction(xDisplay, maskGC, GXor);
5371     plane = 1;
5372     /* Might be quicker to download an XImage and create bitmap
5373        data from it rather than this N copies per piece, but it
5374        only takes a fraction of a second and there is a much
5375        longer delay for loading the pieces.             */
5376     for (n = 0; n < pieceDepth; n ++) {
5377       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5378                  0, 0, squareSize, squareSize,
5379                  0, 0, plane);
5380       plane = plane << 1;
5381     }
5382   }
5383   /* Clean up */
5384   XFreePixmap(xDisplay, buf);
5385   XFreeGC(xDisplay, bufGC);
5386   XFreeGC(xDisplay, maskGC);
5387 }
5388
5389 static void
5390 InitAnimState (AnimNr anr, XWindowAttributes *info)
5391 {
5392   XtGCMask  mask;
5393   XGCValues values;
5394
5395   /* Each buffer is square size, same depth as window */
5396   animBufs[anr+4] = xBoardWindow;
5397   animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
5398                         squareSize, squareSize, info->depth);
5399   animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
5400                         squareSize, squareSize, info->depth);
5401
5402   /* Create a plain GC for blitting */
5403   mask = GCForeground | GCBackground | GCFunction |
5404          GCPlaneMask | GCGraphicsExposures;
5405   values.foreground = XBlackPixel(xDisplay, xScreen);
5406   values.background = XWhitePixel(xDisplay, xScreen);
5407   values.function   = GXcopy;
5408   values.plane_mask = AllPlanes;
5409   values.graphics_exposures = False;
5410   animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5411
5412   /* Piece will be copied from an existing context at
5413      the start of each new animation/drag. */
5414   animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5415
5416   /* Outline will be a read-only copy of an existing */
5417   animGCs[anr+4] = None;
5418 }
5419
5420 void
5421 CreateAnimVars ()
5422 {
5423   XWindowAttributes info;
5424
5425   if (xpmDone && gameInfo.variant == oldVariant) return;
5426   if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
5427   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5428
5429   InitAnimState(Game, &info);
5430   InitAnimState(Player, &info);
5431
5432   /* For XPM pieces, we need bitmaps to use as masks. */
5433   if (useImages)
5434     CreateAnimMasks(info.depth), xpmDone = 1;
5435 }
5436
5437 #ifndef HAVE_USLEEP
5438
5439 static Boolean frameWaiting;
5440
5441 static RETSIGTYPE
5442 FrameAlarm (int sig)
5443 {
5444   frameWaiting = False;
5445   /* In case System-V style signals.  Needed?? */
5446   signal(SIGALRM, FrameAlarm);
5447 }
5448
5449 void
5450 FrameDelay (int time)
5451 {
5452   struct itimerval delay;
5453
5454   XSync(xDisplay, False);
5455
5456   if (time > 0) {
5457     frameWaiting = True;
5458     signal(SIGALRM, FrameAlarm);
5459     delay.it_interval.tv_sec =
5460       delay.it_value.tv_sec = time / 1000;
5461     delay.it_interval.tv_usec =
5462       delay.it_value.tv_usec = (time % 1000) * 1000;
5463     setitimer(ITIMER_REAL, &delay, NULL);
5464     while (frameWaiting) pause();
5465     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5466     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5467     setitimer(ITIMER_REAL, &delay, NULL);
5468   }
5469 }
5470
5471 #else
5472
5473 void
5474 FrameDelay (int time)
5475 {
5476   XSync(xDisplay, False);
5477   if (time > 0)
5478     usleep(time * 1000);
5479 }
5480
5481 #endif
5482
5483 static void
5484 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
5485 {
5486   GC source;
5487
5488   /* Bitmap for piece being moved. */
5489   if (appData.monoMode) {
5490       *mask = *pieceToSolid(piece);
5491   } else if (useImages) {
5492 #if HAVE_LIBXPM
5493       *mask = xpmMask[piece];
5494 #else
5495       *mask = ximMaskPm[piece];
5496 #endif
5497   } else {
5498       *mask = *pieceToSolid(piece);
5499   }
5500
5501   /* GC for piece being moved. Square color doesn't matter, but
5502      since it gets modified we make a copy of the original. */
5503   if (White(piece)) {
5504     if (appData.monoMode)
5505       source = bwPieceGC;
5506     else
5507       source = wlPieceGC;
5508   } else {
5509     if (appData.monoMode)
5510       source = wbPieceGC;
5511     else
5512       source = blPieceGC;
5513   }
5514   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5515
5516   /* Outline only used in mono mode and is not modified */
5517   if (White(piece))
5518     *outline = bwPieceGC;
5519   else
5520     *outline = wbPieceGC;
5521 }
5522
5523 static void
5524 OverlayPiece (ChessSquare piece, GC clip, GC outline,  Drawable dest)
5525 {
5526   int   kind;
5527
5528   if (!useImages) {
5529     /* Draw solid rectangle which will be clipped to shape of piece */
5530     XFillRectangle(xDisplay, dest, clip,
5531                    0, 0, squareSize, squareSize);
5532     if (appData.monoMode)
5533       /* Also draw outline in contrasting color for black
5534          on black / white on white cases                */
5535       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5536                  0, 0, squareSize, squareSize, 0, 0, 1);
5537   } else {
5538     /* Copy the piece */
5539     if (White(piece))
5540       kind = 0;
5541     else
5542       kind = 2;
5543     if(appData.upsideDown && flipView) kind ^= 2;
5544     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5545               dest, clip,
5546               0, 0, squareSize, squareSize,
5547               0, 0);
5548   }
5549 }
5550
5551 void
5552 InsertPiece (AnimNr anr, ChessSquare piece)
5553 {
5554   OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
5555 }
5556
5557 void
5558 DrawBlank (AnimNr anr, int x, int y, int startColor)
5559 {
5560     BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
5561 }
5562
5563 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
5564                  int srcX, int srcY, int width, int height, int destX, int destY)
5565 {
5566     XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
5567                 srcX, srcY, width, height, destX, destY);
5568 }
5569
5570 void
5571 SetDragPiece (AnimNr anr, ChessSquare piece)
5572 {
5573   Pixmap mask;
5574   /* The piece will be drawn using its own bitmap as a matte    */
5575   SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
5576   XSetClipMask(xDisplay, animGCs[anr+2], mask);
5577 }
5578
5579 #include <sys/ioctl.h>
5580 int
5581 get_term_width ()
5582 {
5583     int fd, default_width;
5584
5585     fd = STDIN_FILENO;
5586     default_width = 79; // this is FICS default anyway...
5587
5588 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
5589     struct ttysize win;
5590     if (!ioctl(fd, TIOCGSIZE, &win))
5591         default_width = win.ts_cols;
5592 #elif defined(TIOCGWINSZ)
5593     struct winsize win;
5594     if (!ioctl(fd, TIOCGWINSZ, &win))
5595         default_width = win.ws_col;
5596 #endif
5597     return default_width;
5598 }
5599
5600 void
5601 update_ics_width ()
5602 {
5603   static int old_width = 0;
5604   int new_width = get_term_width();
5605
5606   if (old_width != new_width)
5607     ics_printf("set width %d\n", new_width);
5608   old_width = new_width;
5609 }
5610
5611 void
5612 NotifyFrontendLogin ()
5613 {
5614     update_ics_width();
5615 }
5616
5617 /* [AS] Arrow highlighting support */
5618
5619 void
5620 DrawPolygon (Pnt arrow[], int nr)
5621 {
5622     XPoint pts[10];
5623     int i;
5624     for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
5625     XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
5626     if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
5627 }
5628
5629 void
5630 UpdateLogos (int displ)
5631 {
5632     return; // no logos in XBoard yet
5633 }
5634