Redo Engine Output window with generic popup
[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 "engineoutput.h"
210 #include "usystem.h"
211 #include "gettext.h"
212
213
214 #ifdef __EMX__
215 #ifndef HAVE_USLEEP
216 #define HAVE_USLEEP
217 #endif
218 #define usleep(t)   _sleep2(((t)+500)/1000)
219 #endif
220
221 #ifdef ENABLE_NLS
222 # define  _(s) gettext (s)
223 # define N_(s) gettext_noop (s)
224 #else
225 # define  _(s) (s)
226 # define N_(s)  s
227 #endif
228
229 int main P((int argc, char **argv));
230 RETSIGTYPE CmailSigHandler P((int sig));
231 RETSIGTYPE IntSigHandler P((int sig));
232 RETSIGTYPE TermSizeSigHandler P((int sig));
233 static void CreateGCs P((int redo));
234 static void CreateAnyPieces P((void));
235 void CreateXIMPieces P((void));
236 void CreateXPMPieces P((void));
237 void CreateXPMBoard P((char *s, int n));
238 void CreatePieces P((void));
239 void CreatePieceMenus P((void));
240 Widget CreateMenuBar P((Menu *mb, int boardWidth));
241 Widget CreateButtonBar P ((MenuItem *mi));
242 #if ENABLE_NLS
243 char *InsertPxlSize P((char *pattern, int targetPxlSize));
244 XFontSet CreateFontSet P((char *base_fnt_lst));
245 #else
246 char *FindFont P((char *pattern, int targetPxlSize));
247 #endif
248 void PieceMenuPopup P((Widget w, XEvent *event,
249                        String *params, Cardinal *num_params));
250 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
251 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
252 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
253                    u_int wreq, u_int hreq));
254 void CreateGrid P((void));
255 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
256 void DelayedDrag P((void));
257 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
258 void HandleUserMove P((Widget w, XEvent *event,
259                      String *prms, Cardinal *nprms));
260 void AnimateUserMove P((Widget w, XEvent * event,
261                      String * params, Cardinal * nParams));
262 void HandlePV P((Widget w, XEvent * event,
263                      String * params, Cardinal * nParams));
264 void WhiteClock P((Widget w, XEvent *event,
265                    String *prms, Cardinal *nprms));
266 void BlackClock P((Widget w, XEvent *event,
267                    String *prms, Cardinal *nprms));
268 void DrawPositionProc P((Widget w, XEvent *event,
269                      String *prms, Cardinal *nprms));
270 void CommentClick P((Widget w, XEvent * event,
271                    String * params, Cardinal * nParams));
272 void ICSInputBoxPopUp P((void));
273 void FileNamePopUp P((char *label, char *def, char *filter,
274                       FileProc proc, char *openMode));
275 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
276 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
277 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
278 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
279 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
280 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
281 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
282 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
283 Boolean TempBackwardActive = False;
284 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
285 void DisplayMove P((int moveNumber));
286 void ICSInitScript P((void));
287 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
288 void update_ics_width P(());
289 int get_term_width P(());
290 int CopyMemoProc P(());
291
292 /*
293 * XBoard depends on Xt R4 or higher
294 */
295 int xtVersion = XtSpecificationRelease;
296
297 int xScreen;
298 Display *xDisplay;
299 Window xBoardWindow;
300 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
301   highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
302 Pixel lowTimeWarningColor;
303 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
304   bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
305   prelineGC, countGC;
306 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
307 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
308   whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
309   commentShell, whitePieceMenu, blackPieceMenu, dropMenu,
310   menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
311   ICSInputShell, fileNameShell;
312 Widget historyShell, evalGraphShell;
313 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
314 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
315 #if ENABLE_NLS
316 XFontSet fontSet, clockFontSet;
317 #else
318 Font clockFontID;
319 XFontStruct *clockFontStruct;
320 #endif
321 Font coordFontID, countFontID;
322 XFontStruct *coordFontStruct, *countFontStruct;
323 XtAppContext appContext;
324 char *layoutName;
325
326 FileProc fileProc;
327 char *fileOpenMode;
328 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
329
330 Position commentX = -1, commentY = -1;
331 Dimension commentW, commentH;
332 typedef unsigned int BoardSize;
333 BoardSize boardSize;
334 Boolean chessProgram;
335
336 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
337 int smallLayout = 0, tinyLayout = 0,
338   marginW, marginH, // [HGM] for run-time resizing
339   fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
340   ICSInputBoxUp = False,
341   filenameUp = False, pmFromX = -1, pmFromY = -1,
342   errorExitStatus = -1, defaultLineGap;
343 Dimension textHeight;
344 Pixel timerForegroundPixel, timerBackgroundPixel;
345 Pixel buttonForegroundPixel, buttonBackgroundPixel;
346 char *chessDir, *programName, *programVersion;
347 Boolean alwaysOnTop = False;
348 char *icsTextMenuString;
349 char *icsNames;
350 char *firstChessProgramNames;
351 char *secondChessProgramNames;
352
353 WindowPlacement wpMain;
354 WindowPlacement wpConsole;
355 WindowPlacement wpComment;
356 WindowPlacement wpMoveHistory;
357 WindowPlacement wpEvalGraph;
358 WindowPlacement wpEngineOutput;
359 WindowPlacement wpGameList;
360 WindowPlacement wpTags;
361
362
363 #define SOLID 0
364 #define OUTLINE 1
365 Pixmap pieceBitmap[2][(int)BlackPawn];
366 Pixmap pieceBitmap2[2][(int)BlackPawn+4];       /* [HGM] pieces */
367 Pixmap xpmPieceBitmap[4][(int)BlackPawn];       /* LL, LD, DL, DD actually used*/
368 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4];    /* LL, LD, DL, DD set to select from */
369 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
370 Pixmap xpmBoardBitmap[2];
371 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
372 XImage *ximPieceBitmap[4][(int)BlackPawn+4];    /* LL, LD, DL, DD */
373 Pixmap ximMaskPm[(int)BlackPawn];               /* clipmasks, used for XIM pieces */
374 Pixmap ximMaskPm2[(int)BlackPawn+4];            /* clipmasks, used for XIM pieces */
375 XImage *ximLightSquare, *ximDarkSquare;
376 XImage *xim_Cross;
377
378 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
379 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
380
381 #define White(piece) ((int)(piece) < (int)BlackPawn)
382
383 /* Bitmaps for use as masks when drawing XPM pieces.
384    Need one for each black and white piece.             */
385 static Pixmap xpmMask[BlackKing + 1];
386
387 /* This magic number is the number of intermediate frames used
388    in each half of the animation. For short moves it's reduced
389    by 1. The total number of frames will be factor * 2 + 1.  */
390 #define kFactor    4
391
392 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
393
394 #define PAUSE_BUTTON "P"
395 MenuItem buttonBar[] = {
396     {"<<", "<<", ToStartEvent},
397     {"<", "<", BackwardEvent},
398     {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
399     {">", ">", ForwardEvent},
400     {">>", ">>", ToEndEvent},
401     {NULL, NULL, NULL}
402 };
403
404 #define PIECE_MENU_SIZE 18
405 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
406     { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
407       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
408       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
409       N_("Empty square"), N_("Clear board") },
410     { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
411       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
412       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
413       N_("Empty square"), N_("Clear board") }
414 };
415 /* must be in same order as pieceMenuStrings! */
416 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
417     { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
418         WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
419         WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
420         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
421     { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
422         BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
423         BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
424         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
425 };
426
427 #define DROP_MENU_SIZE 6
428 String dropMenuStrings[DROP_MENU_SIZE] = {
429     "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
430   };
431 /* must be in same order as dropMenuStrings! */
432 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
433     (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
434     WhiteRook, WhiteQueen
435 };
436
437 typedef struct {
438     char piece;
439     char* widget;
440 } DropMenuEnables;
441
442 DropMenuEnables dmEnables[] = {
443     { 'P', "Pawn" },
444     { 'N', "Knight" },
445     { 'B', "Bishop" },
446     { 'R', "Rook" },
447     { 'Q', "Queen" }
448 };
449
450 Arg shellArgs[] = {
451     { XtNwidth, 0 },
452     { XtNheight, 0 },
453     { XtNminWidth, 0 },
454     { XtNminHeight, 0 },
455     { XtNmaxWidth, 0 },
456     { XtNmaxHeight, 0 }
457 };
458
459 Arg layoutArgs[] = {
460     { XtNborderWidth, 0 },
461     { XtNdefaultDistance, 0 },
462 };
463
464 Arg formArgs[] = {
465     { XtNborderWidth, 0 },
466     { XtNresizable, (XtArgVal) True },
467 };
468
469 Arg boardArgs[] = {
470     { XtNborderWidth, 0 },
471     { XtNwidth, 0 },
472     { XtNheight, 0 }
473 };
474
475 Arg titleArgs[] = {
476     { XtNjustify, (XtArgVal) XtJustifyRight },
477     { XtNlabel, (XtArgVal) "..." },
478     { XtNresizable, (XtArgVal) True },
479     { XtNresize, (XtArgVal) False }
480 };
481
482 Arg messageArgs[] = {
483     { XtNjustify, (XtArgVal) XtJustifyLeft },
484     { XtNlabel, (XtArgVal) "..." },
485     { XtNresizable, (XtArgVal) True },
486     { XtNresize, (XtArgVal) False }
487 };
488
489 Arg timerArgs[] = {
490     { XtNborderWidth, 0 },
491     { XtNjustify, (XtArgVal) XtJustifyLeft }
492 };
493
494 XtResource clientResources[] = {
495     { "flashCount", "flashCount", XtRInt, sizeof(int),
496         XtOffset(AppDataPtr, flashCount), XtRImmediate,
497         (XtPointer) FLASH_COUNT  },
498 };
499
500 XrmOptionDescRec shellOptions[] = {
501     { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
502     { "-flash", "flashCount", XrmoptionNoArg, "3" },
503     { "-xflash", "flashCount", XrmoptionNoArg, "0" },
504 };
505
506 XtActionsRec boardActions[] = {
507     { "DrawPosition", DrawPositionProc },
508     { "HandleUserMove", HandleUserMove },
509     { "AnimateUserMove", AnimateUserMove },
510     { "HandlePV", HandlePV },
511     { "SelectPV", SelectPV },
512     { "StopPV", StopPV },
513     { "PieceMenuPopup", PieceMenuPopup },
514     { "WhiteClock", WhiteClock },
515     { "BlackClock", BlackClock },
516     { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
517     { "QuitProc", QuitWrapper },
518     { "ManProc", ManInner },
519     { "TempBackwardProc", TempBackwardProc },
520     { "TempForwardProc", TempForwardProc },
521     { "CommentClick", (XtActionProc) CommentClick },
522     { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
523     { "GenericPopDown", (XtActionProc) GenericPopDown },
524     { "ErrorPopDown", (XtActionProc) ErrorPopDown },
525     { "CopyMemoProc", (XtActionProc) CopyMemoProc },
526     { "SelectMove", (XtActionProc) SelectMove },
527     { "LoadSelectedProc", LoadSelectedProc },
528     { "SetFilterProc", SetFilterProc },
529     { "TypeInProc", TypeInProc },
530     { "EnterKeyProc", EnterKeyProc },
531     { "UpKeyProc", UpKeyProc },
532     { "DownKeyProc", DownKeyProc },
533     { "WheelProc", WheelProc },
534     { "TabProc", TabProc },
535 };
536
537 char globalTranslations[] =
538   ":<Key>F9: MenuItem(ResignProc) \n \
539    :Ctrl<Key>n: MenuItem(NewGame) \n \
540    :Meta<Key>V: MenuItem(NewVariant) \n \
541    :Ctrl<Key>o: MenuItem(LoadGame) \n \
542    :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
543    :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
544    :Ctrl<Key>Down: LoadSelectedProc(3) \n \
545    :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
546    :Ctrl<Key>s: MenuItem(SaveGame) \n \
547    :Ctrl<Key>c: MenuItem(CopyGame) \n \
548    :Ctrl<Key>v: MenuItem(PasteGame) \n \
549    :Ctrl<Key>O: MenuItem(LoadPosition) \n \
550    :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
551    :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
552    :Ctrl<Key>S: MenuItem(SavePosition) \n \
553    :Ctrl<Key>C: MenuItem(CopyPosition) \n \
554    :Ctrl<Key>V: MenuItem(PastePosition) \n \
555    :Ctrl<Key>q: MenuItem(Exit) \n \
556    :Ctrl<Key>w: MenuItem(MachineWhite) \n \
557    :Ctrl<Key>b: MenuItem(MachineBlack) \n \
558    :Ctrl<Key>t: MenuItem(TwoMachines) \n \
559    :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
560    :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
561    :Ctrl<Key>e: MenuItem(EditGame) \n \
562    :Ctrl<Key>E: MenuItem(EditPosition) \n \
563    :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
564    :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
565    :Meta<Key>G: MenuItem(ShowGameList) \n \
566    :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
567    :<Key>Pause: MenuItem(Pause) \n \
568    :<Key>F3: MenuItem(Accept) \n \
569    :<Key>F4: MenuItem(Decline) \n \
570    :<Key>F12: MenuItem(Rematch) \n \
571    :<Key>F5: MenuItem(CallFlag) \n \
572    :<Key>F6: MenuItem(Draw) \n \
573    :<Key>F7: MenuItem(Adjourn) \n \
574    :<Key>F8: MenuItem(Abort) \n \
575    :<Key>F10: MenuItem(StopObserving) \n \
576    :<Key>F11: MenuItem(StopExamining) \n \
577    :Ctrl<Key>d: MenuItem(DebugProc) \n \
578    :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
579    :Meta<Key>End: MenuItem(ToEnd) \n \
580    :Meta<Key>Right: MenuItem(Forward) \n \
581    :Meta<Key>Home: MenuItem(ToStart) \n \
582    :Meta<Key>Left: MenuItem(Backward) \n \
583    :<Key>Left: MenuItem(Backward) \n \
584    :<Key>Right: MenuItem(Forward) \n \
585    :<Key>Home: MenuItem(Revert) \n \
586    :<Key>End: MenuItem(TruncateGame) \n \
587    :Ctrl<Key>m: MenuItem(MoveNow) \n \
588    :Ctrl<Key>x: MenuItem(RetractMove) \n \
589    :Meta<Key>J: MenuItem(Adjudications) \n \
590    :Meta<Key>U: MenuItem(CommonEngine) \n \
591    :Meta<Key>T: MenuItem(TimeControl) \n \
592    :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
593 #ifndef OPTIONSDIALOG
594     "\
595    :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
596    :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
597    :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
598    :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
599    :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
600 #endif
601    "\
602    :<Key>F1: MenuItem(Manual) \n \
603    :<Key>F2: MenuItem(FlipView) \n \
604    :<KeyDown>Return: TempBackwardProc() \n \
605    :<KeyUp>Return: TempForwardProc() \n";
606
607 char boardTranslations[] =
608    "<Btn1Down>: HandleUserMove(0) \n \
609    Shift<Btn1Up>: HandleUserMove(1) \n \
610    <Btn1Up>: HandleUserMove(0) \n \
611    <Btn1Motion>: AnimateUserMove() \n \
612    <Btn3Motion>: HandlePV() \n \
613    <Btn2Motion>: HandlePV() \n \
614    <Btn3Up>: PieceMenuPopup(menuB) \n \
615    <Btn2Up>: PieceMenuPopup(menuB) \n \
616    Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
617                  PieceMenuPopup(menuB) \n \
618    Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
619                  PieceMenuPopup(menuW) \n \
620    Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
621                  PieceMenuPopup(menuW) \n \
622    Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
623                  PieceMenuPopup(menuB) \n";
624
625 char whiteTranslations[] =
626    "Shift<BtnDown>: WhiteClock(1)\n \
627    <BtnDown>: WhiteClock(0)\n";
628 char blackTranslations[] =
629    "Shift<BtnDown>: BlackClock(1)\n \
630    <BtnDown>: BlackClock(0)\n";
631
632 char ICSInputTranslations[] =
633     "<Key>Up: UpKeyProc() \n "
634     "<Key>Down: DownKeyProc() \n "
635     "<Key>Return: EnterKeyProc() \n";
636
637 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
638 //             as the widget is destroyed before the up-click can call extend-end
639 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
640
641 String xboardResources[] = {
642     "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
643     NULL
644   };
645
646
647 /* Max possible square size */
648 #define MAXSQSIZE 256
649
650 static int xpm_avail[MAXSQSIZE];
651
652 #ifdef HAVE_DIR_STRUCT
653
654 /* Extract piece size from filename */
655 static int
656 xpm_getsize (char *name, int len, char *ext)
657 {
658     char *p, *d;
659     char buf[10];
660
661     if (len < 4)
662       return 0;
663
664     if ((p=strchr(name, '.')) == NULL ||
665         StrCaseCmp(p+1, ext) != 0)
666       return 0;
667
668     p = name + 3;
669     d = buf;
670
671     while (*p && isdigit(*p))
672       *(d++) = *(p++);
673
674     *d = 0;
675     return atoi(buf);
676 }
677
678 /* Setup xpm_avail */
679 static int
680 xpm_getavail (char *dirname, char *ext)
681 {
682     DIR *dir;
683     struct dirent *ent;
684     int  i;
685
686     for (i=0; i<MAXSQSIZE; ++i)
687       xpm_avail[i] = 0;
688
689     if (appData.debugMode)
690       fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
691
692     dir = opendir(dirname);
693     if (!dir)
694       {
695           fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
696                   programName, dirname);
697           exit(1);
698       }
699
700     while ((ent=readdir(dir)) != NULL) {
701         i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
702         if (i > 0 && i < MAXSQSIZE)
703           xpm_avail[i] = 1;
704     }
705
706     closedir(dir);
707
708     return 0;
709 }
710
711 void
712 xpm_print_avail (FILE *fp, char *ext)
713 {
714     int i;
715
716     fprintf(fp, _("Available `%s' sizes:\n"), ext);
717     for (i=1; i<MAXSQSIZE; ++i) {
718         if (xpm_avail[i])
719           printf("%d\n", i);
720     }
721 }
722
723 /* Return XPM piecesize closest to size */
724 int
725 xpm_closest_to (char *dirname, int size, char *ext)
726 {
727     int i;
728     int sm_diff = MAXSQSIZE;
729     int sm_index = 0;
730     int diff;
731
732     xpm_getavail(dirname, ext);
733
734     if (appData.debugMode)
735       xpm_print_avail(stderr, ext);
736
737     for (i=1; i<MAXSQSIZE; ++i) {
738         if (xpm_avail[i]) {
739             diff = size - i;
740             diff = (diff<0) ? -diff : diff;
741             if (diff < sm_diff) {
742                 sm_diff = diff;
743                 sm_index = i;
744             }
745         }
746     }
747
748     if (!sm_index) {
749         fprintf(stderr, _("Error: No `%s' files!\n"), ext);
750         exit(1);
751     }
752
753     return sm_index;
754 }
755 #else   /* !HAVE_DIR_STRUCT */
756 /* If we are on a system without a DIR struct, we can't
757    read the directory, so we can't collect a list of
758    filenames, etc., so we can't do any size-fitting. */
759 int
760 xpm_closest_to (char *dirname, int size, char *ext)
761 {
762     fprintf(stderr, _("\
763 Warning: No DIR structure found on this system --\n\
764          Unable to autosize for XPM/XIM pieces.\n\
765    Please report this error to %s.\n\
766    Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
767     return size;
768 }
769 #endif /* HAVE_DIR_STRUCT */
770
771
772 /* Arrange to catch delete-window events */
773 Atom wm_delete_window;
774 void
775 CatchDeleteWindow (Widget w, String procname)
776 {
777   char buf[MSG_SIZ];
778   XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
779   snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
780   XtAugmentTranslations(w, XtParseTranslationTable(buf));
781 }
782
783 void
784 BoardToTop ()
785 {
786   Arg args[16];
787   XtSetArg(args[0], XtNiconic, False);
788   XtSetValues(shellWidget, args, 1);
789
790   XtPopup(shellWidget, XtGrabNone); /* Raise if lowered  */
791 }
792
793 //---------------------------------------------------------------------------------------------------------
794 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
795 #define XBOARD True
796 #define JAWS_ARGS
797 #define CW_USEDEFAULT (1<<31)
798 #define ICS_TEXT_MENU_SIZE 90
799 #define DEBUG_FILE "xboard.debug"
800 #define SetCurrentDirectory chdir
801 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
802 #define OPTCHAR "-"
803 #define SEPCHAR " "
804
805 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
806 #include "args.h"
807
808 // front-end part of option handling
809
810 // [HGM] This platform-dependent table provides the location for storing the color info
811 extern char *crWhite, * crBlack;
812
813 void *
814 colorVariable[] = {
815   &appData.whitePieceColor,
816   &appData.blackPieceColor,
817   &appData.lightSquareColor,
818   &appData.darkSquareColor,
819   &appData.highlightSquareColor,
820   &appData.premoveHighlightColor,
821   &appData.lowTimeWarningColor,
822   NULL,
823   NULL,
824   NULL,
825   NULL,
826   NULL,
827   &crWhite,
828   &crBlack,
829   NULL
830 };
831
832 // [HGM] font: keep a font for each square size, even non-stndard ones
833 #define NUM_SIZES 18
834 #define MAX_SIZE 130
835 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
836 char *fontTable[NUM_FONTS][MAX_SIZE];
837
838 void
839 ParseFont (char *name, int number)
840 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
841   int size;
842   if(sscanf(name, "size%d:", &size)) {
843     // [HGM] font: font is meant for specific boardSize (likely from settings file);
844     //       defer processing it until we know if it matches our board size
845     if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
846         fontTable[number][size] = strdup(strchr(name, ':')+1);
847         fontValid[number][size] = True;
848     }
849     return;
850   }
851   switch(number) {
852     case 0: // CLOCK_FONT
853         appData.clockFont = strdup(name);
854       break;
855     case 1: // MESSAGE_FONT
856         appData.font = strdup(name);
857       break;
858     case 2: // COORD_FONT
859         appData.coordFont = strdup(name);
860       break;
861     default:
862       return;
863   }
864   fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
865 }
866
867 void
868 SetFontDefaults ()
869 { // only 2 fonts currently
870   appData.clockFont = CLOCK_FONT_NAME;
871   appData.coordFont = COORD_FONT_NAME;
872   appData.font  =   DEFAULT_FONT_NAME;
873 }
874
875 void
876 CreateFonts ()
877 { // no-op, until we identify the code for this already in XBoard and move it here
878 }
879
880 void
881 ParseColor (int n, char *name)
882 { // in XBoard, just copy the color-name string
883   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
884 }
885
886 void
887 ParseTextAttribs (ColorClass cc, char *s)
888 {
889     (&appData.colorShout)[cc] = strdup(s);
890 }
891
892 void
893 ParseBoardSize (void *addr, char *name)
894 {
895     appData.boardSize = strdup(name);
896 }
897
898 void
899 LoadAllSounds ()
900 { // In XBoard the sound-playing program takes care of obtaining the actual sound
901 }
902
903 void
904 SetCommPortDefaults ()
905 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
906 }
907
908 // [HGM] args: these three cases taken out to stay in front-end
909 void
910 SaveFontArg (FILE *f, ArgDescriptor *ad)
911 {
912   char *name;
913   int i, n = (int)(intptr_t)ad->argLoc;
914   switch(n) {
915     case 0: // CLOCK_FONT
916         name = appData.clockFont;
917       break;
918     case 1: // MESSAGE_FONT
919         name = appData.font;
920       break;
921     case 2: // COORD_FONT
922         name = appData.coordFont;
923       break;
924     default:
925       return;
926   }
927   for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
928     if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
929         fontTable[n][squareSize] = strdup(name);
930         fontValid[n][squareSize] = True;
931         break;
932   }
933   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
934     fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
935 }
936
937 void
938 ExportSounds ()
939 { // nothing to do, as the sounds are at all times represented by their text-string names already
940 }
941
942 void
943 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
944 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
945         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
946 }
947
948 void
949 SaveColor (FILE *f, ArgDescriptor *ad)
950 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
951         if(colorVariable[(int)(intptr_t)ad->argLoc])
952         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
953 }
954
955 void
956 SaveBoardSize (FILE *f, char *name, void *addr)
957 { // wrapper to shield back-end from BoardSize & sizeInfo
958   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
959 }
960
961 void
962 ParseCommPortSettings (char *s)
963 { // no such option in XBoard (yet)
964 }
965
966 int frameX, frameY;
967
968 void
969 GetActualPlacement (Widget wg, WindowPlacement *wp)
970 {
971   Arg args[16];
972   Dimension w, h;
973   Position x, y;
974   XWindowAttributes winAt;
975   Window win, dummy;
976   int i, rx, ry;
977
978   if(!wg) return;
979
980     win = XtWindow(wg);
981     XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
982     XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
983     wp->x = rx - winAt.x;
984     wp->y = ry - winAt.y;
985     wp->height = winAt.height;
986     wp->width = winAt.width;
987     frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
988 }
989
990 void
991 GetWindowCoords ()
992 { // wrapper to shield use of window handles from back-end (make addressible by number?)
993   // In XBoard this will have to wait until awareness of window parameters is implemented
994   GetActualPlacement(shellWidget, &wpMain);
995   if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
996   if(MoveHistoryIsUp()) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
997   if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
998   if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
999   if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
1000   if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1001 }
1002
1003 void
1004 PrintCommPortSettings (FILE *f, char *name)
1005 { // This option does not exist in XBoard
1006 }
1007
1008 void
1009 EnsureOnScreen (int *x, int *y, int minX, int minY)
1010 {
1011   return;
1012 }
1013
1014 int
1015 MainWindowUp ()
1016 { // [HGM] args: allows testing if main window is realized from back-end
1017   return xBoardWindow != 0;
1018 }
1019
1020 void
1021 PopUpStartupDialog ()
1022 {  // start menu not implemented in XBoard
1023 }
1024
1025 char *
1026 ConvertToLine (int argc, char **argv)
1027 {
1028   static char line[128*1024], buf[1024];
1029   int i;
1030
1031   line[0] = NULLCHAR;
1032   for(i=1; i<argc; i++)
1033     {
1034       if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1035           && argv[i][0] != '{' )
1036         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1037       else
1038         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1039       strncat(line, buf, 128*1024 - strlen(line) - 1 );
1040     }
1041
1042   line[strlen(line)-1] = NULLCHAR;
1043   return line;
1044 }
1045
1046 //--------------------------------------------------------------------------------------------
1047
1048 #ifdef IDSIZES
1049   // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1050 #else
1051 #define BoardSize int
1052 void
1053 InitDrawingSizes (BoardSize boardSize, int flags)
1054 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1055     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1056     Arg args[16];
1057     XtGeometryResult gres;
1058     int i;
1059     static Dimension oldWidth, oldHeight;
1060     static VariantClass oldVariant;
1061     static int oldDual = -1, oldMono = -1;
1062
1063     if(!formWidget) return;
1064
1065     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1066     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1067     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1068
1069   if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1070     /*
1071      * Enable shell resizing.
1072      */
1073     shellArgs[0].value = (XtArgVal) &w;
1074     shellArgs[1].value = (XtArgVal) &h;
1075     XtGetValues(shellWidget, shellArgs, 2);
1076
1077     shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1078     shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1079     XtSetValues(shellWidget, &shellArgs[2], 4);
1080
1081     XtSetArg(args[0], XtNdefaultDistance, &sep);
1082     XtGetValues(formWidget, args, 1);
1083
1084     oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1085     CreateGrid();
1086     hOffset = boardWidth + 10;
1087     for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1088         secondSegments[i] = gridSegments[i];
1089         secondSegments[i].x1 += hOffset;
1090         secondSegments[i].x2 += hOffset;
1091     }
1092
1093     XtSetArg(args[0], XtNwidth, boardWidth);
1094     XtSetArg(args[1], XtNheight, boardHeight);
1095     XtSetValues(boardWidget, args, 2);
1096
1097     timerWidth = (boardWidth - sep) / 2;
1098     XtSetArg(args[0], XtNwidth, timerWidth);
1099     XtSetValues(whiteTimerWidget, args, 1);
1100     XtSetValues(blackTimerWidget, args, 1);
1101
1102     XawFormDoLayout(formWidget, False);
1103
1104     if (appData.titleInWindow) {
1105         i = 0;
1106         XtSetArg(args[i], XtNborderWidth, &bor); i++;
1107         XtSetArg(args[i], XtNheight, &h);  i++;
1108         XtGetValues(titleWidget, args, i);
1109         if (smallLayout) {
1110             w = boardWidth - 2*bor;
1111         } else {
1112             XtSetArg(args[0], XtNwidth, &w);
1113             XtGetValues(menuBarWidget, args, 1);
1114             w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1115         }
1116
1117         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1118         if (gres != XtGeometryYes && appData.debugMode) {
1119             fprintf(stderr,
1120                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1121                     programName, gres, w, h, wr, hr);
1122         }
1123     }
1124
1125     XawFormDoLayout(formWidget, True);
1126
1127     /*
1128      * Inhibit shell resizing.
1129      */
1130     shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1131     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1132     shellArgs[4].value = shellArgs[2].value = w;
1133     shellArgs[5].value = shellArgs[3].value = h;
1134     XtSetValues(shellWidget, &shellArgs[0], 6);
1135
1136     XSync(xDisplay, False);
1137     DelayedDrag();
1138   }
1139
1140     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1141     // (only for xpm)
1142
1143   if(gameInfo.variant != oldVariant) { // and only if variant changed
1144
1145     if(useImages) {
1146       for(i=0; i<4; i++) {
1147         int p;
1148         for(p=0; p<=(int)WhiteKing; p++)
1149            xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1150         if(gameInfo.variant == VariantShogi) {
1151            xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1152            xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1153            xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1154            xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1155            xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1156         }
1157 #ifdef GOTHIC
1158         if(gameInfo.variant == VariantGothic) {
1159            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1160         }
1161 #endif
1162         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1163            xpmPieceBitmap[i][(int)WhiteAngel]    = xpmPieceBitmap2[i][(int)WhiteFalcon];
1164            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1165         }
1166 #if !HAVE_LIBXPM
1167         // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1168         for(p=0; p<=(int)WhiteKing; p++)
1169            ximMaskPm[p] = ximMaskPm2[p]; // defaults
1170         if(gameInfo.variant == VariantShogi) {
1171            ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1172            ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1173            ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1174            ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1175            ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1176         }
1177 #ifdef GOTHIC
1178         if(gameInfo.variant == VariantGothic) {
1179            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1180         }
1181 #endif
1182         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1183            ximMaskPm[(int)WhiteAngel]    = ximMaskPm2[(int)WhiteFalcon];
1184            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1185         }
1186 #endif
1187       }
1188     } else {
1189       for(i=0; i<2; i++) {
1190         int p;
1191         for(p=0; p<=(int)WhiteKing; p++)
1192            pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1193         if(gameInfo.variant == VariantShogi) {
1194            pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1195            pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1196            pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1197            pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1198            pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1199         }
1200 #ifdef GOTHIC
1201         if(gameInfo.variant == VariantGothic) {
1202            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1203         }
1204 #endif
1205         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1206            pieceBitmap[i][(int)WhiteAngel]    = pieceBitmap2[i][(int)WhiteFalcon];
1207            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1208         }
1209       }
1210     }
1211     oldMono = -10; // kludge to force recreation of animation masks
1212     oldVariant = gameInfo.variant;
1213   }
1214 #if HAVE_LIBXPM
1215   if(appData.monoMode != oldMono)
1216     CreateAnimVars();
1217 #endif
1218   oldMono = appData.monoMode;
1219 }
1220 #endif
1221
1222 static int
1223 MakeOneColor (char *name, Pixel *color)
1224 {
1225     XrmValue vFrom, vTo;
1226     if (!appData.monoMode) {
1227         vFrom.addr = (caddr_t) name;
1228         vFrom.size = strlen(name);
1229         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1230         if (vTo.addr == NULL) {
1231           appData.monoMode = True;
1232           return True;
1233         } else {
1234           *color = *(Pixel *) vTo.addr;
1235         }
1236     }
1237     return False;
1238 }
1239
1240 static int
1241 MakeColors ()
1242 {   // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1243     int forceMono = False;
1244
1245     forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1246     forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1247     forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1248     forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1249     forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1250     forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1251     if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1252     if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1253
1254     return forceMono;
1255 }
1256
1257 static void
1258 CreateAnyPieces ()
1259 {   // [HGM] taken out of main
1260 #if HAVE_LIBXPM
1261     if (appData.monoMode && // [HGM] no sense to go on to certain doom
1262        (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1263             appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1264
1265     if (appData.bitmapDirectory[0] != NULLCHAR) {
1266       CreatePieces();
1267     } else {
1268       CreateXPMPieces();
1269       CreateXPMBoard(appData.liteBackTextureFile, 1);
1270       CreateXPMBoard(appData.darkBackTextureFile, 0);
1271     }
1272 #else
1273     CreateXIMPieces();
1274     /* Create regular pieces */
1275     if (!useImages) CreatePieces();
1276 #endif
1277 }
1278
1279 void
1280 InitDrawingParams ()
1281 {
1282     MakeColors(); CreateGCs(True);
1283     CreateAnyPieces();
1284 }
1285
1286 int
1287 main (int argc, char **argv)
1288 {
1289     int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1290     XSetWindowAttributes window_attributes;
1291     Arg args[16];
1292     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1293     XrmValue vFrom, vTo;
1294     XtGeometryResult gres;
1295     char *p;
1296     XrmDatabase xdb;
1297     int forceMono = False;
1298
1299     srandom(time(0)); // [HGM] book: make random truly random
1300
1301     setbuf(stdout, NULL);
1302     setbuf(stderr, NULL);
1303     debugFP = stderr;
1304
1305     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1306         printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1307         exit(0);
1308     }
1309
1310     programName = strrchr(argv[0], '/');
1311     if (programName == NULL)
1312       programName = argv[0];
1313     else
1314       programName++;
1315
1316 #ifdef ENABLE_NLS
1317     XtSetLanguageProc(NULL, NULL, NULL);
1318     bindtextdomain(PACKAGE, LOCALEDIR);
1319     textdomain(PACKAGE);
1320 #endif
1321
1322     shellWidget =
1323       XtAppInitialize(&appContext, "XBoard", shellOptions,
1324                       XtNumber(shellOptions),
1325                       &argc, argv, xboardResources, NULL, 0);
1326     appData.boardSize = "";
1327     InitAppData(ConvertToLine(argc, argv));
1328     p = getenv("HOME");
1329     if (p == NULL) p = "/tmp";
1330     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1331     gameCopyFilename = (char*) malloc(i);
1332     gamePasteFilename = (char*) malloc(i);
1333     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1334     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1335
1336     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1337                               clientResources, XtNumber(clientResources),
1338                               NULL, 0);
1339
1340     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1341         static char buf[MSG_SIZ];
1342         EscapeExpand(buf, appData.firstInitString);
1343         appData.firstInitString = strdup(buf);
1344         EscapeExpand(buf, appData.secondInitString);
1345         appData.secondInitString = strdup(buf);
1346         EscapeExpand(buf, appData.firstComputerString);
1347         appData.firstComputerString = strdup(buf);
1348         EscapeExpand(buf, appData.secondComputerString);
1349         appData.secondComputerString = strdup(buf);
1350     }
1351
1352     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1353         chessDir = ".";
1354     } else {
1355         if (chdir(chessDir) != 0) {
1356             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1357             perror(chessDir);
1358             exit(1);
1359         }
1360     }
1361
1362     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1363         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1364         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
1365            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1366            exit(errno);
1367         }
1368         setbuf(debugFP, NULL);
1369     }
1370
1371 #if ENABLE_NLS
1372     if (appData.debugMode) {
1373       fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1374     }
1375 #endif
1376
1377     /* [HGM,HR] make sure board size is acceptable */
1378     if(appData.NrFiles > BOARD_FILES ||
1379        appData.NrRanks > BOARD_RANKS   )
1380          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1381
1382 #if !HIGHDRAG
1383     /* This feature does not work; animation needs a rewrite */
1384     appData.highlightDragging = FALSE;
1385 #endif
1386     InitBackEnd1();
1387
1388     xDisplay = XtDisplay(shellWidget);
1389     xScreen = DefaultScreen(xDisplay);
1390     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1391
1392         gameInfo.variant = StringToVariant(appData.variant);
1393         InitPosition(FALSE);
1394
1395 #ifdef IDSIZE
1396     InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1397 #else
1398     if (isdigit(appData.boardSize[0])) {
1399         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1400                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1401                    &fontPxlSize, &smallLayout, &tinyLayout);
1402         if (i == 0) {
1403             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1404                     programName, appData.boardSize);
1405             exit(2);
1406         }
1407         if (i < 7) {
1408             /* Find some defaults; use the nearest known size */
1409             SizeDefaults *szd, *nearest;
1410             int distance = 99999;
1411             nearest = szd = sizeDefaults;
1412             while (szd->name != NULL) {
1413                 if (abs(szd->squareSize - squareSize) < distance) {
1414                     nearest = szd;
1415                     distance = abs(szd->squareSize - squareSize);
1416                     if (distance == 0) break;
1417                 }
1418                 szd++;
1419             }
1420             if (i < 2) lineGap = nearest->lineGap;
1421             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1422             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1423             if (i < 5) fontPxlSize = nearest->fontPxlSize;
1424             if (i < 6) smallLayout = nearest->smallLayout;
1425             if (i < 7) tinyLayout = nearest->tinyLayout;
1426         }
1427     } else {
1428         SizeDefaults *szd = sizeDefaults;
1429         if (*appData.boardSize == NULLCHAR) {
1430             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1431                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1432               szd++;
1433             }
1434             if (szd->name == NULL) szd--;
1435             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1436         } else {
1437             while (szd->name != NULL &&
1438                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1439             if (szd->name == NULL) {
1440                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1441                         programName, appData.boardSize);
1442                 exit(2);
1443             }
1444         }
1445         squareSize = szd->squareSize;
1446         lineGap = szd->lineGap;
1447         clockFontPxlSize = szd->clockFontPxlSize;
1448         coordFontPxlSize = szd->coordFontPxlSize;
1449         fontPxlSize = szd->fontPxlSize;
1450         smallLayout = szd->smallLayout;
1451         tinyLayout = szd->tinyLayout;
1452         // [HGM] font: use defaults from settings file if available and not overruled
1453     }
1454     if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1455         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1456     if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1457         appData.font = fontTable[MESSAGE_FONT][squareSize];
1458     if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1459         appData.coordFont = fontTable[COORD_FONT][squareSize];
1460
1461     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1462     if (strlen(appData.pixmapDirectory) > 0) {
1463         p = ExpandPathName(appData.pixmapDirectory);
1464         if (!p) {
1465             fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1466                    appData.pixmapDirectory);
1467             exit(1);
1468         }
1469         if (appData.debugMode) {
1470           fprintf(stderr, _("\
1471 XBoard square size (hint): %d\n\
1472 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1473         }
1474         squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1475         if (appData.debugMode) {
1476             fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1477         }
1478     }
1479     defaultLineGap = lineGap;
1480     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1481
1482     /* [HR] height treated separately (hacked) */
1483     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1484     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1485         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1486         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1487
1488     /*
1489      * Determine what fonts to use.
1490      */
1491 #if ENABLE_NLS
1492     appData.font = InsertPxlSize(appData.font, fontPxlSize);
1493     appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1494     appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1495     fontSet = CreateFontSet(appData.font);
1496     clockFontSet = CreateFontSet(appData.clockFont);
1497     {
1498       /* For the coordFont, use the 0th font of the fontset. */
1499       XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1500       XFontStruct **font_struct_list;
1501       XFontSetExtents *fontSize;
1502       char **font_name_list;
1503       XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1504       coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1505       coordFontStruct = XQueryFont(xDisplay, coordFontID);
1506       fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1507       textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1508     }
1509 #else
1510     appData.font = FindFont(appData.font, fontPxlSize);
1511     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1512     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1513     clockFontID = XLoadFont(xDisplay, appData.clockFont);
1514     clockFontStruct = XQueryFont(xDisplay, clockFontID);
1515     coordFontID = XLoadFont(xDisplay, appData.coordFont);
1516     coordFontStruct = XQueryFont(xDisplay, coordFontID);
1517 #endif
1518     countFontID = coordFontID;  // [HGM] holdings
1519     countFontStruct = coordFontStruct;
1520
1521     xdb = XtDatabase(xDisplay);
1522 #if ENABLE_NLS
1523     XrmPutLineResource(&xdb, "*international: True");
1524     vTo.size = sizeof(XFontSet);
1525     vTo.addr = (XtPointer) &fontSet;
1526     XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1527 #else
1528     XrmPutStringResource(&xdb, "*font", appData.font);
1529 #endif
1530
1531     /*
1532      * Detect if there are not enough colors available and adapt.
1533      */
1534     if (DefaultDepth(xDisplay, xScreen) <= 2) {
1535       appData.monoMode = True;
1536     }
1537
1538     forceMono = MakeColors();
1539
1540     if (forceMono) {
1541       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1542               programName);
1543         appData.monoMode = True;
1544     }
1545
1546     if (appData.lowTimeWarning && !appData.monoMode) {
1547       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1548       vFrom.size = strlen(appData.lowTimeWarningColor);
1549       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1550       if (vTo.addr == NULL)
1551                 appData.monoMode = True;
1552       else
1553                 lowTimeWarningColor = *(Pixel *) vTo.addr;
1554     }
1555
1556     if (appData.monoMode && appData.debugMode) {
1557         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1558                 (unsigned long) XWhitePixel(xDisplay, xScreen),
1559                 (unsigned long) XBlackPixel(xDisplay, xScreen));
1560     }
1561
1562     ParseIcsTextColors();
1563
1564     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1565
1566     /*
1567      * widget hierarchy
1568      */
1569     if (tinyLayout) {
1570         layoutName = "tinyLayout";
1571     } else if (smallLayout) {
1572         layoutName = "smallLayout";
1573     } else {
1574         layoutName = "normalLayout";
1575     }
1576     /* Outer layoutWidget is there only to provide a name for use in
1577        resources that depend on the layout style */
1578     layoutWidget =
1579       XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1580                             layoutArgs, XtNumber(layoutArgs));
1581     formWidget =
1582       XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1583                             formArgs, XtNumber(formArgs));
1584     XtSetArg(args[0], XtNdefaultDistance, &sep);
1585     XtGetValues(formWidget, args, 1);
1586
1587     j = 0;
1588     widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1589     XtSetArg(args[0], XtNtop,    XtChainTop);
1590     XtSetArg(args[1], XtNbottom, XtChainTop);
1591     XtSetArg(args[2], XtNright,  XtChainLeft);
1592     XtSetValues(menuBarWidget, args, 3);
1593
1594     widgetList[j++] = whiteTimerWidget =
1595       XtCreateWidget("whiteTime", labelWidgetClass,
1596                      formWidget, timerArgs, XtNumber(timerArgs));
1597 #if ENABLE_NLS
1598     XtSetArg(args[0], XtNfontSet, clockFontSet);
1599 #else
1600     XtSetArg(args[0], XtNfont, clockFontStruct);
1601 #endif
1602     XtSetArg(args[1], XtNtop,    XtChainTop);
1603     XtSetArg(args[2], XtNbottom, XtChainTop);
1604     XtSetValues(whiteTimerWidget, args, 3);
1605
1606     widgetList[j++] = blackTimerWidget =
1607       XtCreateWidget("blackTime", labelWidgetClass,
1608                      formWidget, timerArgs, XtNumber(timerArgs));
1609 #if ENABLE_NLS
1610     XtSetArg(args[0], XtNfontSet, clockFontSet);
1611 #else
1612     XtSetArg(args[0], XtNfont, clockFontStruct);
1613 #endif
1614     XtSetArg(args[1], XtNtop,    XtChainTop);
1615     XtSetArg(args[2], XtNbottom, XtChainTop);
1616     XtSetValues(blackTimerWidget, args, 3);
1617
1618     if (appData.titleInWindow) {
1619         widgetList[j++] = titleWidget =
1620           XtCreateWidget("title", labelWidgetClass, formWidget,
1621                          titleArgs, XtNumber(titleArgs));
1622         XtSetArg(args[0], XtNtop,    XtChainTop);
1623         XtSetArg(args[1], XtNbottom, XtChainTop);
1624         XtSetValues(titleWidget, args, 2);
1625     }
1626
1627     if (appData.showButtonBar) {
1628       widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1629       XtSetArg(args[0], XtNleft,  XtChainRight); // [HGM] glue to right window edge
1630       XtSetArg(args[1], XtNright, XtChainRight); //       for good run-time sizing
1631       XtSetArg(args[2], XtNtop,    XtChainTop);
1632       XtSetArg(args[3], XtNbottom, XtChainTop);
1633       XtSetValues(buttonBarWidget, args, 4);
1634     }
1635
1636     widgetList[j++] = messageWidget =
1637       XtCreateWidget("message", labelWidgetClass, formWidget,
1638                      messageArgs, XtNumber(messageArgs));
1639     XtSetArg(args[0], XtNtop,    XtChainTop);
1640     XtSetArg(args[1], XtNbottom, XtChainTop);
1641     XtSetValues(messageWidget, args, 2);
1642
1643     widgetList[j++] = boardWidget =
1644       XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1645                      XtNumber(boardArgs));
1646
1647     XtManageChildren(widgetList, j);
1648
1649     timerWidth = (boardWidth - sep) / 2;
1650     XtSetArg(args[0], XtNwidth, timerWidth);
1651     XtSetValues(whiteTimerWidget, args, 1);
1652     XtSetValues(blackTimerWidget, args, 1);
1653
1654     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1655     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1656     XtGetValues(whiteTimerWidget, args, 2);
1657
1658     if (appData.showButtonBar) {
1659       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1660       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1661       XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1662     }
1663
1664     /*
1665      * formWidget uses these constraints but they are stored
1666      * in the children.
1667      */
1668     i = 0;
1669     XtSetArg(args[i], XtNfromHoriz, 0); i++;
1670     XtSetValues(menuBarWidget, args, i);
1671     if (appData.titleInWindow) {
1672         if (smallLayout) {
1673             i = 0;
1674             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1675             XtSetValues(whiteTimerWidget, args, i);
1676             i = 0;
1677             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1678             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1679             XtSetValues(blackTimerWidget, args, i);
1680             i = 0;
1681             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1682             XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1683             XtSetValues(titleWidget, args, i);
1684             i = 0;
1685             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1686             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1687             XtSetValues(messageWidget, args, i);
1688             if (appData.showButtonBar) {
1689               i = 0;
1690               XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1691               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1692               XtSetValues(buttonBarWidget, args, i);
1693             }
1694         } else {
1695             i = 0;
1696             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1697             XtSetValues(whiteTimerWidget, args, i);
1698             i = 0;
1699             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1700             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1701             XtSetValues(blackTimerWidget, args, i);
1702             i = 0;
1703             XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1704             XtSetValues(titleWidget, args, i);
1705             i = 0;
1706             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1707             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1708             XtSetValues(messageWidget, args, i);
1709             if (appData.showButtonBar) {
1710               i = 0;
1711               XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1712               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1713               XtSetValues(buttonBarWidget, args, i);
1714             }
1715         }
1716     } else {
1717         i = 0;
1718         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1719         XtSetValues(whiteTimerWidget, args, i);
1720         i = 0;
1721         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1722         XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1723         XtSetValues(blackTimerWidget, args, i);
1724         i = 0;
1725         XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1726         XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1727         XtSetValues(messageWidget, args, i);
1728         if (appData.showButtonBar) {
1729           i = 0;
1730           XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1731           XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1732           XtSetValues(buttonBarWidget, args, i);
1733         }
1734     }
1735     i = 0;
1736     XtSetArg(args[0], XtNfromVert, messageWidget);
1737     XtSetArg(args[1], XtNtop,    XtChainTop);
1738     XtSetArg(args[2], XtNbottom, XtChainBottom);
1739     XtSetArg(args[3], XtNleft,   XtChainLeft);
1740     XtSetArg(args[4], XtNright,  XtChainRight);
1741     XtSetValues(boardWidget, args, 5);
1742
1743     XtRealizeWidget(shellWidget);
1744
1745     if(wpMain.x > 0) {
1746       XtSetArg(args[0], XtNx, wpMain.x);
1747       XtSetArg(args[1], XtNy, wpMain.y);
1748       XtSetValues(shellWidget, args, 2);
1749     }
1750
1751     /*
1752      * Correct the width of the message and title widgets.
1753      * It is not known why some systems need the extra fudge term.
1754      * The value "2" is probably larger than needed.
1755      */
1756     XawFormDoLayout(formWidget, False);
1757
1758 #define WIDTH_FUDGE 2
1759     i = 0;
1760     XtSetArg(args[i], XtNborderWidth, &bor);  i++;
1761     XtSetArg(args[i], XtNheight, &h);  i++;
1762     XtGetValues(messageWidget, args, i);
1763     if (appData.showButtonBar) {
1764       i = 0;
1765       XtSetArg(args[i], XtNwidth, &w);  i++;
1766       XtGetValues(buttonBarWidget, args, i);
1767       w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1768     } else {
1769       w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1770     }
1771
1772     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1773     if (gres != XtGeometryYes && appData.debugMode) {
1774       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1775               programName, gres, w, h, wr, hr);
1776     }
1777
1778     /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1779     /* The size used for the child widget in layout lags one resize behind
1780        its true size, so we resize a second time, 1 pixel smaller.  Yeech! */
1781     w--;
1782     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1783     if (gres != XtGeometryYes && appData.debugMode) {
1784       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1785               programName, gres, w, h, wr, hr);
1786     }
1787     /* !! end hack */
1788     if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1789     XtSetArg(args[0], XtNleft,  XtChainLeft);  // [HGM] glue ends for good run-time sizing
1790     XtSetArg(args[1], XtNright, XtChainRight);
1791     XtSetValues(messageWidget, args, 2);
1792
1793     if (appData.titleInWindow) {
1794         i = 0;
1795         XtSetArg(args[i], XtNborderWidth, &bor); i++;
1796         XtSetArg(args[i], XtNheight, &h);  i++;
1797         XtGetValues(titleWidget, args, i);
1798         if (smallLayout) {
1799             w = boardWidth - 2*bor;
1800         } else {
1801             XtSetArg(args[0], XtNwidth, &w);
1802             XtGetValues(menuBarWidget, args, 1);
1803             w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1804         }
1805
1806         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1807         if (gres != XtGeometryYes && appData.debugMode) {
1808             fprintf(stderr,
1809                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1810                     programName, gres, w, h, wr, hr);
1811         }
1812     }
1813     XawFormDoLayout(formWidget, True);
1814
1815     xBoardWindow = XtWindow(boardWidget);
1816
1817     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1818     //       not need to go into InitDrawingSizes().
1819 #endif
1820
1821     /*
1822      * Create X checkmark bitmap and initialize option menu checks.
1823      */
1824     ReadBitmap(&xMarkPixmap, "checkmark.bm",
1825                checkmark_bits, checkmark_width, checkmark_height);
1826     InitMenuMarkers();
1827
1828     /*
1829      * Create an icon.
1830      */
1831     ReadBitmap(&wIconPixmap, "icon_white.bm",
1832                icon_white_bits, icon_white_width, icon_white_height);
1833     ReadBitmap(&bIconPixmap, "icon_black.bm",
1834                icon_black_bits, icon_black_width, icon_black_height);
1835     iconPixmap = wIconPixmap;
1836     i = 0;
1837     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
1838     XtSetValues(shellWidget, args, i);
1839
1840     /*
1841      * Create a cursor for the board widget.
1842      */
1843     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1844     XChangeWindowAttributes(xDisplay, xBoardWindow,
1845                             CWCursor, &window_attributes);
1846
1847     /*
1848      * Inhibit shell resizing.
1849      */
1850     shellArgs[0].value = (XtArgVal) &w;
1851     shellArgs[1].value = (XtArgVal) &h;
1852     XtGetValues(shellWidget, shellArgs, 2);
1853     shellArgs[4].value = shellArgs[2].value = w;
1854     shellArgs[5].value = shellArgs[3].value = h;
1855     XtSetValues(shellWidget, &shellArgs[2], 4);
1856     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1857     marginH =  h - boardHeight;
1858
1859     CatchDeleteWindow(shellWidget, "QuitProc");
1860
1861     CreateGCs(False);
1862     CreateGrid();
1863     CreateAnyPieces();
1864
1865     CreatePieceMenus();
1866
1867     if (appData.animate || appData.animateDragging)
1868       CreateAnimVars();
1869
1870     XtAugmentTranslations(formWidget,
1871                           XtParseTranslationTable(globalTranslations));
1872     XtAugmentTranslations(boardWidget,
1873                           XtParseTranslationTable(boardTranslations));
1874     XtAugmentTranslations(whiteTimerWidget,
1875                           XtParseTranslationTable(whiteTranslations));
1876     XtAugmentTranslations(blackTimerWidget,
1877                           XtParseTranslationTable(blackTranslations));
1878
1879     /* Why is the following needed on some versions of X instead
1880      * of a translation? */
1881     XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
1882                       (XtEventHandler) EventProc, NULL);
1883     /* end why */
1884     XtAddEventHandler(formWidget, KeyPressMask, False,
1885                       (XtEventHandler) MoveTypeInProc, NULL);
1886     XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1887                       (XtEventHandler) EventProc, NULL);
1888
1889     /* [AS] Restore layout */
1890     if( wpMoveHistory.visible ) {
1891       HistoryPopUp();
1892     }
1893
1894     if( wpEvalGraph.visible )
1895       {
1896         EvalGraphPopUp();
1897       };
1898
1899     if( wpEngineOutput.visible ) {
1900       EngineOutputPopUp();
1901     }
1902
1903     InitBackEnd2();
1904
1905     if (errorExitStatus == -1) {
1906         if (appData.icsActive) {
1907             /* We now wait until we see "login:" from the ICS before
1908                sending the logon script (problems with timestamp otherwise) */
1909             /*ICSInitScript();*/
1910             if (appData.icsInputBox) ICSInputBoxPopUp();
1911         }
1912
1913     #ifdef SIGWINCH
1914     signal(SIGWINCH, TermSizeSigHandler);
1915     #endif
1916         signal(SIGINT, IntSigHandler);
1917         signal(SIGTERM, IntSigHandler);
1918         if (*appData.cmailGameName != NULLCHAR) {
1919             signal(SIGUSR1, CmailSigHandler);
1920         }
1921     }
1922
1923     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1924     InitPosition(TRUE);
1925 //    XtSetKeyboardFocus(shellWidget, formWidget);
1926     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1927
1928     XtAppMainLoop(appContext);
1929     if (appData.debugMode) fclose(debugFP); // [DM] debug
1930     return 0;
1931 }
1932
1933 RETSIGTYPE
1934 TermSizeSigHandler (int sig)
1935 {
1936     update_ics_width();
1937 }
1938
1939 RETSIGTYPE
1940 IntSigHandler (int sig)
1941 {
1942     ExitEvent(sig);
1943 }
1944
1945 RETSIGTYPE
1946 CmailSigHandler (int sig)
1947 {
1948     int dummy = 0;
1949     int error;
1950
1951     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
1952
1953     /* Activate call-back function CmailSigHandlerCallBack()             */
1954     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1955
1956     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1957 }
1958
1959 void
1960 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1961 {
1962     BoardToTop();
1963     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
1964 }
1965 /**** end signal code ****/
1966
1967
1968 #define Abs(n) ((n)<0 ? -(n) : (n))
1969
1970 #ifdef ENABLE_NLS
1971 char *
1972 InsertPxlSize (char *pattern, int targetPxlSize)
1973 {
1974     char *base_fnt_lst, strInt[12], *p, *q;
1975     int alternatives, i, len, strIntLen;
1976
1977     /*
1978      * Replace the "*" (if present) in the pixel-size slot of each
1979      * alternative with the targetPxlSize.
1980      */
1981     p = pattern;
1982     alternatives = 1;
1983     while ((p = strchr(p, ',')) != NULL) {
1984       alternatives++;
1985       p++;
1986     }
1987     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1988     strIntLen = strlen(strInt);
1989     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1990
1991     p = pattern;
1992     q = base_fnt_lst;
1993     while (alternatives--) {
1994       char *comma = strchr(p, ',');
1995       for (i=0; i<14; i++) {
1996         char *hyphen = strchr(p, '-');
1997         if (!hyphen) break;
1998         if (comma && hyphen > comma) break;
1999         len = hyphen + 1 - p;
2000         if (i == 7 && *p == '*' && len == 2) {
2001           p += len;
2002           memcpy(q, strInt, strIntLen);
2003           q += strIntLen;
2004           *q++ = '-';
2005         } else {
2006           memcpy(q, p, len);
2007           p += len;
2008           q += len;
2009         }
2010       }
2011       if (!comma) break;
2012       len = comma + 1 - p;
2013       memcpy(q, p, len);
2014       p += len;
2015       q += len;
2016     }
2017     strcpy(q, p);
2018
2019     return base_fnt_lst;
2020 }
2021
2022 XFontSet
2023 CreateFontSet (char *base_fnt_lst)
2024 {
2025     XFontSet fntSet;
2026     char **missing_list;
2027     int missing_count;
2028     char *def_string;
2029
2030     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2031                             &missing_list, &missing_count, &def_string);
2032     if (appData.debugMode) {
2033       int i, count;
2034       XFontStruct **font_struct_list;
2035       char **font_name_list;
2036       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2037       if (fntSet) {
2038         fprintf(debugFP, " got list %s, locale %s\n",
2039                 XBaseFontNameListOfFontSet(fntSet),
2040                 XLocaleOfFontSet(fntSet));
2041         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2042         for (i = 0; i < count; i++) {
2043           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2044         }
2045       }
2046       for (i = 0; i < missing_count; i++) {
2047         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2048       }
2049     }
2050     if (fntSet == NULL) {
2051       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2052       exit(2);
2053     }
2054     return fntSet;
2055 }
2056 #else // not ENABLE_NLS
2057 /*
2058  * Find a font that matches "pattern" that is as close as
2059  * possible to the targetPxlSize.  Prefer fonts that are k
2060  * pixels smaller to fonts that are k pixels larger.  The
2061  * pattern must be in the X Consortium standard format,
2062  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2063  * The return value should be freed with XtFree when no
2064  * longer needed.
2065  */
2066 char *
2067 FindFont (char *pattern, int targetPxlSize)
2068 {
2069     char **fonts, *p, *best, *scalable, *scalableTail;
2070     int i, j, nfonts, minerr, err, pxlSize;
2071
2072     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2073     if (nfonts < 1) {
2074         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2075                 programName, pattern);
2076         exit(2);
2077     }
2078
2079     best = fonts[0];
2080     scalable = NULL;
2081     minerr = 999999;
2082     for (i=0; i<nfonts; i++) {
2083         j = 0;
2084         p = fonts[i];
2085         if (*p != '-') continue;
2086         while (j < 7) {
2087             if (*p == NULLCHAR) break;
2088             if (*p++ == '-') j++;
2089         }
2090         if (j < 7) continue;
2091         pxlSize = atoi(p);
2092         if (pxlSize == 0) {
2093             scalable = fonts[i];
2094             scalableTail = p;
2095         } else {
2096             err = pxlSize - targetPxlSize;
2097             if (Abs(err) < Abs(minerr) ||
2098                 (minerr > 0 && err < 0 && -err == minerr)) {
2099                 best = fonts[i];
2100                 minerr = err;
2101             }
2102         }
2103     }
2104     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2105         /* If the error is too big and there is a scalable font,
2106            use the scalable font. */
2107         int headlen = scalableTail - scalable;
2108         p = (char *) XtMalloc(strlen(scalable) + 10);
2109         while (isdigit(*scalableTail)) scalableTail++;
2110         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2111     } else {
2112         p = (char *) XtMalloc(strlen(best) + 2);
2113         safeStrCpy(p, best, strlen(best)+1 );
2114     }
2115     if (appData.debugMode) {
2116         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
2117                 pattern, targetPxlSize, p);
2118     }
2119     XFreeFontNames(fonts);
2120     return p;
2121 }
2122 #endif
2123
2124 void
2125 DeleteGCs ()
2126 {   // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2127     // must be called before all non-first callse to CreateGCs()
2128     XtReleaseGC(shellWidget, highlineGC);
2129     XtReleaseGC(shellWidget, lightSquareGC);
2130     XtReleaseGC(shellWidget, darkSquareGC);
2131     XtReleaseGC(shellWidget, lineGC);
2132     if (appData.monoMode) {
2133         if (DefaultDepth(xDisplay, xScreen) == 1) {
2134             XtReleaseGC(shellWidget, wbPieceGC);
2135         } else {
2136             XtReleaseGC(shellWidget, bwPieceGC);
2137         }
2138     } else {
2139         XtReleaseGC(shellWidget, prelineGC);
2140         XtReleaseGC(shellWidget, wdPieceGC);
2141         XtReleaseGC(shellWidget, wlPieceGC);
2142         XtReleaseGC(shellWidget, bdPieceGC);
2143         XtReleaseGC(shellWidget, blPieceGC);
2144     }
2145 }
2146
2147 static GC
2148 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2149 {
2150     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2151       | GCBackground | GCFunction | GCPlaneMask;
2152     gc_values->foreground = foreground;
2153     gc_values->background = background;
2154     return XtGetGC(shellWidget, value_mask, gc_values);
2155 }
2156
2157 static void
2158 CreateGCs (int redo)
2159 {
2160     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2161       | GCBackground | GCFunction | GCPlaneMask;
2162     XGCValues gc_values;
2163     GC copyInvertedGC;
2164     Pixel white = XWhitePixel(xDisplay, xScreen);
2165     Pixel black = XBlackPixel(xDisplay, xScreen);
2166
2167     gc_values.plane_mask = AllPlanes;
2168     gc_values.line_width = lineGap;
2169     gc_values.line_style = LineSolid;
2170     gc_values.function = GXcopy;
2171
2172   if(redo) {
2173     DeleteGCs(); // called a second time; clean up old GCs first
2174   } else { // [HGM] grid and font GCs created on first call only
2175     coordGC = CreateOneGC(&gc_values, black, white);
2176     XSetFont(xDisplay, coordGC, coordFontID);
2177
2178     // [HGM] make font for holdings counts (white on black)
2179     countGC = CreateOneGC(&gc_values, white, black);
2180     XSetFont(xDisplay, countGC, countFontID);
2181   }
2182     lineGC = CreateOneGC(&gc_values, black, black);
2183
2184     if (appData.monoMode) {
2185
2186         highlineGC = CreateOneGC(&gc_values, white, white);
2187         lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2188         darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2189
2190         if (DefaultDepth(xDisplay, xScreen) == 1) {
2191             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2192             gc_values.function = GXcopyInverted;
2193             copyInvertedGC = CreateOneGC(&gc_values, black, white);
2194             gc_values.function = GXcopy;
2195             if (XBlackPixel(xDisplay, xScreen) == 1) {
2196                 bwPieceGC = darkSquareGC;
2197                 wbPieceGC = copyInvertedGC;
2198             } else {
2199                 bwPieceGC = copyInvertedGC;
2200                 wbPieceGC = lightSquareGC;
2201             }
2202         }
2203     } else {
2204
2205         highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2206         prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2207         lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2208         darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2209         wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2210         wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2211         bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2212         blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2213     }
2214 }
2215
2216 void
2217 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2218 {
2219     int x, y, w, h, p;
2220     FILE *fp;
2221     Pixmap temp;
2222     XGCValues   values;
2223     GC maskGC;
2224
2225     fp = fopen(filename, "rb");
2226     if (!fp) {
2227         fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2228         exit(1);
2229     }
2230
2231     w = fgetc(fp);
2232     h = fgetc(fp);
2233
2234     for (y=0; y<h; ++y) {
2235         for (x=0; x<h; ++x) {
2236             p = fgetc(fp);
2237
2238             switch (p) {
2239               case 0:
2240                 XPutPixel(xim, x, y, blackPieceColor);
2241                 if (xmask)
2242                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2243                 break;
2244               case 1:
2245                 XPutPixel(xim, x, y, darkSquareColor);
2246                 if (xmask)
2247                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2248                 break;
2249               case 2:
2250                 XPutPixel(xim, x, y, whitePieceColor);
2251                 if (xmask)
2252                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2253                 break;
2254               case 3:
2255                 XPutPixel(xim, x, y, lightSquareColor);
2256                 if (xmask)
2257                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2258                 break;
2259             }
2260         }
2261     }
2262
2263     fclose(fp);
2264
2265     /* create Pixmap of piece */
2266     *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2267                           w, h, xim->depth);
2268     XPutImage(xDisplay, *dest, lightSquareGC, xim,
2269               0, 0, 0, 0, w, h);
2270
2271     /* create Pixmap of clipmask
2272        Note: We assume the white/black pieces have the same
2273              outline, so we make only 6 masks. This is okay
2274              since the XPM clipmask routines do the same. */
2275     if (xmask) {
2276       temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2277                             w, h, xim->depth);
2278       XPutImage(xDisplay, temp, lightSquareGC, xmask,
2279               0, 0, 0, 0, w, h);
2280
2281       /* now create the 1-bit version */
2282       *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2283                           w, h, 1);
2284
2285       values.foreground = 1;
2286       values.background = 0;
2287
2288       /* Don't use XtGetGC, not read only */
2289       maskGC = XCreateGC(xDisplay, *mask,
2290                     GCForeground | GCBackground, &values);
2291       XCopyPlane(xDisplay, temp, *mask, maskGC,
2292                   0, 0, squareSize, squareSize, 0, 0, 1);
2293       XFreePixmap(xDisplay, temp);
2294     }
2295 }
2296
2297
2298 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2299
2300 void
2301 CreateXIMPieces ()
2302 {
2303     int piece, kind;
2304     char buf[MSG_SIZ];
2305     u_int ss;
2306     static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2307     XImage *ximtemp;
2308
2309     ss = squareSize;
2310
2311     /* The XSynchronize calls were copied from CreatePieces.
2312        Not sure if needed, but can't hurt */
2313     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2314                                      buffering bug */
2315
2316     /* temp needed by loadXIM() */
2317     ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2318                  0, 0, ss, ss, AllPlanes, XYPixmap);
2319
2320     if (strlen(appData.pixmapDirectory) == 0) {
2321       useImages = 0;
2322     } else {
2323         useImages = 1;
2324         if (appData.monoMode) {
2325           DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2326                             0, 2);
2327           ExitEvent(2);
2328         }
2329         fprintf(stderr, _("\nLoading XIMs...\n"));
2330         /* Load pieces */
2331         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2332             fprintf(stderr, "%d", piece+1);
2333             for (kind=0; kind<4; kind++) {
2334                 fprintf(stderr, ".");
2335                 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2336                         ExpandPathName(appData.pixmapDirectory),
2337                         piece <= (int) WhiteKing ? "" : "w",
2338                         pieceBitmapNames[piece],
2339                         ximkind[kind], ss);
2340                 ximPieceBitmap[kind][piece] =
2341                   XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2342                             0, 0, ss, ss, AllPlanes, XYPixmap);
2343                 if (appData.debugMode)
2344                   fprintf(stderr, _("(File:%s:) "), buf);
2345                 loadXIM(ximPieceBitmap[kind][piece],
2346                         ximtemp, buf,
2347                         &(xpmPieceBitmap2[kind][piece]),
2348                         &(ximMaskPm2[piece]));
2349                 if(piece <= (int)WhiteKing)
2350                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2351             }
2352             fprintf(stderr," ");
2353         }
2354         /* Load light and dark squares */
2355         /* If the LSQ and DSQ pieces don't exist, we will
2356            draw them with solid squares. */
2357         snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2358         if (access(buf, 0) != 0) {
2359             useImageSqs = 0;
2360         } else {
2361             useImageSqs = 1;
2362             fprintf(stderr, _("light square "));
2363             ximLightSquare=
2364               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2365                         0, 0, ss, ss, AllPlanes, XYPixmap);
2366             if (appData.debugMode)
2367               fprintf(stderr, _("(File:%s:) "), buf);
2368
2369             loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2370             fprintf(stderr, _("dark square "));
2371             snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2372                     ExpandPathName(appData.pixmapDirectory), ss);
2373             if (appData.debugMode)
2374               fprintf(stderr, _("(File:%s:) "), buf);
2375             ximDarkSquare=
2376               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2377                         0, 0, ss, ss, AllPlanes, XYPixmap);
2378             loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2379             xpmJailSquare = xpmLightSquare;
2380         }
2381         fprintf(stderr, _("Done.\n"));
2382     }
2383     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2384 }
2385
2386 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2387
2388 #if HAVE_LIBXPM
2389 void
2390 CreateXPMBoard (char *s, int kind)
2391 {
2392     XpmAttributes attr;
2393     attr.valuemask = 0;
2394     if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2395     if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2396         useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2397     }
2398 }
2399
2400 void
2401 FreeXPMPieces ()
2402 {   // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2403     // thisroutine has to be called t free the old piece pixmaps
2404     int piece, kind;
2405     for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2406         for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2407     if(useImageSqs) {
2408         XFreePixmap(xDisplay, xpmLightSquare);
2409         XFreePixmap(xDisplay, xpmDarkSquare);
2410     }
2411 }
2412
2413 void
2414 CreateXPMPieces ()
2415 {
2416     int piece, kind, r;
2417     char buf[MSG_SIZ];
2418     u_int ss = squareSize;
2419     XpmAttributes attr;
2420     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2421     XpmColorSymbol symbols[4];
2422     static int redo = False;
2423
2424     if(redo) FreeXPMPieces(); else redo = 1;
2425
2426     /* The XSynchronize calls were copied from CreatePieces.
2427        Not sure if needed, but can't hurt */
2428     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2429
2430     /* Setup translations so piece colors match square colors */
2431     symbols[0].name = "light_piece";
2432     symbols[0].value = appData.whitePieceColor;
2433     symbols[1].name = "dark_piece";
2434     symbols[1].value = appData.blackPieceColor;
2435     symbols[2].name = "light_square";
2436     symbols[2].value = appData.lightSquareColor;
2437     symbols[3].name = "dark_square";
2438     symbols[3].value = appData.darkSquareColor;
2439
2440     attr.valuemask = XpmColorSymbols;
2441     attr.colorsymbols = symbols;
2442     attr.numsymbols = 4;
2443
2444     if (appData.monoMode) {
2445       DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2446                         0, 2);
2447       ExitEvent(2);
2448     }
2449     if (strlen(appData.pixmapDirectory) == 0) {
2450         XpmPieces* pieces = builtInXpms;
2451         useImages = 1;
2452         /* Load pieces */
2453         while (pieces->size != squareSize && pieces->size) pieces++;
2454         if (!pieces->size) {
2455           fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2456           exit(1);
2457         }
2458         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2459             for (kind=0; kind<4; kind++) {
2460
2461                 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2462                                                pieces->xpm[piece][kind],
2463                                                &(xpmPieceBitmap2[kind][piece]),
2464                                                NULL, &attr)) != 0) {
2465                   fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2466                           r, buf);
2467                   exit(1);
2468                 }
2469                 if(piece <= (int) WhiteKing)
2470                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2471             }
2472         }
2473         useImageSqs = 0;
2474         xpmJailSquare = xpmLightSquare;
2475     } else {
2476         useImages = 1;
2477
2478         fprintf(stderr, _("\nLoading XPMs...\n"));
2479
2480         /* Load pieces */
2481         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2482             fprintf(stderr, "%d ", piece+1);
2483             for (kind=0; kind<4; kind++) {
2484               snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2485                         ExpandPathName(appData.pixmapDirectory),
2486                         piece > (int) WhiteKing ? "w" : "",
2487                         pieceBitmapNames[piece],
2488                         xpmkind[kind], ss);
2489                 if (appData.debugMode) {
2490                     fprintf(stderr, _("(File:%s:) "), buf);
2491                 }
2492                 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2493                                            &(xpmPieceBitmap2[kind][piece]),
2494                                            NULL, &attr)) != 0) {
2495                     if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2496                       // [HGM] missing: read of unorthodox piece failed; substitute King.
2497                       snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2498                                 ExpandPathName(appData.pixmapDirectory),
2499                                 xpmkind[kind], ss);
2500                         if (appData.debugMode) {
2501                             fprintf(stderr, _("(Replace by File:%s:) "), buf);
2502                         }
2503                         r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2504                                                 &(xpmPieceBitmap2[kind][piece]),
2505                                                 NULL, &attr);
2506                     }
2507                     if (r != 0) {
2508                         fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2509                                 r, buf);
2510                         exit(1);
2511                     }
2512                 }
2513                 if(piece <= (int) WhiteKing)
2514                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2515             }
2516         }
2517         /* Load light and dark squares */
2518         /* If the LSQ and DSQ pieces don't exist, we will
2519            draw them with solid squares. */
2520         fprintf(stderr, _("light square "));
2521         snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2522         if (access(buf, 0) != 0) {
2523             useImageSqs = 0;
2524         } else {
2525             useImageSqs = 1;
2526             if (appData.debugMode)
2527               fprintf(stderr, _("(File:%s:) "), buf);
2528
2529             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2530                                        &xpmLightSquare, NULL, &attr)) != 0) {
2531                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2532                 exit(1);
2533             }
2534             fprintf(stderr, _("dark square "));
2535             snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2536                     ExpandPathName(appData.pixmapDirectory), ss);
2537             if (appData.debugMode) {
2538                 fprintf(stderr, _("(File:%s:) "), buf);
2539             }
2540             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2541                                        &xpmDarkSquare, NULL, &attr)) != 0) {
2542                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2543                 exit(1);
2544             }
2545         }
2546         xpmJailSquare = xpmLightSquare;
2547         fprintf(stderr, _("Done.\n"));
2548     }
2549     oldVariant = -1; // kludge to force re-makig of animation masks
2550     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2551                                       buffering bug */
2552 }
2553 #endif /* HAVE_LIBXPM */
2554
2555 #if HAVE_LIBXPM
2556 /* No built-in bitmaps */
2557 void CreatePieces()
2558 {
2559     int piece, kind;
2560     char buf[MSG_SIZ];
2561     u_int ss = squareSize;
2562
2563     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2564                                      buffering bug */
2565
2566     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2567         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2568           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2569                    pieceBitmapNames[piece],
2570                    ss, kind == SOLID ? 's' : 'o');
2571           ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2572           if(piece <= (int)WhiteKing)
2573             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2574         }
2575     }
2576
2577     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2578                                       buffering bug */
2579 }
2580 #else
2581 /* With built-in bitmaps */
2582 void
2583 CreatePieces ()
2584 {
2585     BuiltInBits* bib = builtInBits;
2586     int piece, kind;
2587     char buf[MSG_SIZ];
2588     u_int ss = squareSize;
2589
2590     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2591                                      buffering bug */
2592
2593     while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2594
2595     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2596         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2597           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2598                    pieceBitmapNames[piece],
2599                    ss, kind == SOLID ? 's' : 'o');
2600           ReadBitmap(&pieceBitmap2[kind][piece], buf,
2601                      bib->bits[kind][piece], ss, ss);
2602           if(piece <= (int)WhiteKing)
2603             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2604         }
2605     }
2606
2607     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2608                                       buffering bug */
2609 }
2610 #endif
2611
2612 void
2613 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2614 {
2615     int x_hot, y_hot;
2616     u_int w, h;
2617     int errcode;
2618     char msg[MSG_SIZ], fullname[MSG_SIZ];
2619
2620     if (*appData.bitmapDirectory != NULLCHAR) {
2621       safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2622       strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2623       strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2624       errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2625                                 &w, &h, pm, &x_hot, &y_hot);
2626       fprintf(stderr, "load %s\n", name);
2627         if (errcode != BitmapSuccess) {
2628             switch (errcode) {
2629               case BitmapOpenFailed:
2630                 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2631                 break;
2632               case BitmapFileInvalid:
2633                 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2634                 break;
2635               case BitmapNoMemory:
2636                 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2637                         fullname);
2638                 break;
2639               default:
2640                 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2641                         errcode, fullname);
2642                 break;
2643             }
2644             fprintf(stderr, _("%s: %s...using built-in\n"),
2645                     programName, msg);
2646         } else if (w != wreq || h != hreq) {
2647             fprintf(stderr,
2648                     _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2649                     programName, fullname, w, h, wreq, hreq);
2650         } else {
2651             return;
2652         }
2653     }
2654     if (bits != NULL) {
2655         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2656                                     wreq, hreq);
2657     }
2658 }
2659
2660 void
2661 CreateGrid ()
2662 {
2663     int i, j;
2664
2665     if (lineGap == 0) return;
2666
2667     /* [HR] Split this into 2 loops for non-square boards. */
2668
2669     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2670         gridSegments[i].x1 = 0;
2671         gridSegments[i].x2 =
2672           lineGap + BOARD_WIDTH * (squareSize + lineGap);
2673         gridSegments[i].y1 = gridSegments[i].y2
2674           = lineGap / 2 + (i * (squareSize + lineGap));
2675     }
2676
2677     for (j = 0; j < BOARD_WIDTH + 1; j++) {
2678         gridSegments[j + i].y1 = 0;
2679         gridSegments[j + i].y2 =
2680           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2681         gridSegments[j + i].x1 = gridSegments[j + i].x2
2682           = lineGap / 2 + (j * (squareSize + lineGap));
2683     }
2684 }
2685
2686 int nrOfMenuItems = 7;
2687 Widget menuWidget[150];
2688 MenuListItem menuItemList[150] = {
2689     { "LoadNextGameProc", LoadNextGameProc },
2690     { "LoadPrevGameProc", LoadPrevGameProc },
2691     { "ReloadGameProc", ReloadGameProc },
2692     { "ReloadPositionProc", ReloadPositionProc },
2693 #ifndef OPTIONSDIALOG
2694     { "AlwaysQueenProc", AlwaysQueenProc },
2695     { "AnimateDraggingProc", AnimateDraggingProc },
2696     { "AnimateMovingProc", AnimateMovingProc },
2697     { "AutoflagProc", AutoflagProc },
2698     { "AutoflipProc", AutoflipProc },
2699     { "BlindfoldProc", BlindfoldProc },
2700     { "FlashMovesProc", FlashMovesProc },
2701 #if HIGHDRAG
2702     { "HighlightDraggingProc", HighlightDraggingProc },
2703 #endif
2704     { "HighlightLastMoveProc", HighlightLastMoveProc },
2705 //    { "IcsAlarmProc", IcsAlarmProc },
2706     { "MoveSoundProc", MoveSoundProc },
2707     { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2708     { "PopupExitMessageProc", PopupExitMessageProc },
2709     { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2710 //    { "PremoveProc", PremoveProc },
2711     { "ShowCoordsProc", ShowCoordsProc },
2712     { "ShowThinkingProc", ShowThinkingProc },
2713     { "HideThinkingProc", HideThinkingProc },
2714     { "TestLegalityProc", TestLegalityProc },
2715 #endif
2716     { "AboutGameProc", AboutGameEvent },
2717     { "DebugProc", DebugProc },
2718     { "NothingProc", NothingProc },
2719   {NULL, NothingProc}
2720 };
2721
2722 void
2723 MarkMenuItem (char *menuRef, int state)
2724 {
2725     int nr = MenuToNumber(menuRef);
2726     if(nr >= 0) {
2727         Arg args[2];
2728         XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2729         XtSetValues(menuWidget[nr], args, 1);
2730     }
2731 }
2732
2733 void
2734 EnableMenuItem (char *menuRef, int state)
2735 {
2736     int nr = MenuToNumber(menuRef);
2737     if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2738 }
2739
2740 void
2741 EnableButtonBar (int state)
2742 {
2743     XtSetSensitive(buttonBarWidget, state);
2744 }
2745
2746
2747 void
2748 SetMenuEnables (Enables *enab)
2749 {
2750   while (enab->name != NULL) {
2751     EnableMenuItem(enab->name, enab->value);
2752     enab++;
2753   }
2754 }
2755
2756 int
2757 Equal(char *p, char *s)
2758 {   // compare strings skipping spaces in second
2759     while(*s) {
2760         if(*s == ' ') { s++; continue; }
2761         if(*s++ != *p++) return 0;
2762     }
2763     return !*p;
2764 }
2765
2766 void
2767 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2768 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2769     int i;
2770     if(*nprms == 0) return;
2771     for(i=0; menuItemList[i].name; i++) {
2772         if(Equal(prms[0], menuItemList[i].name)) {
2773             (menuItemList[i].proc) ();
2774             return;
2775         }
2776     }
2777 }
2778
2779 static void
2780 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2781 {
2782     MenuProc *proc = (MenuProc *) addr;
2783
2784     (proc)();
2785 }
2786
2787 static void
2788 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2789 {
2790     RecentEngineEvent((int) (intptr_t) addr);
2791 }
2792
2793 // some stuff that must remain in front-end
2794 static Widget mainBar, currentMenu;
2795 static int wtot, nr = 0, widths[10];
2796
2797 void
2798 AppendMenuItem (char *text, char *name, MenuProc *action)
2799 {
2800     int j;
2801     Widget entry;
2802     Arg args[16];
2803
2804     j = 0;
2805     XtSetArg(args[j], XtNleftMargin, 20);   j++;
2806     XtSetArg(args[j], XtNrightMargin, 20);  j++;
2807
2808         if (strcmp(text, "----") == 0) {
2809           entry = XtCreateManagedWidget(text, smeLineObjectClass,
2810                                           currentMenu, args, j);
2811         } else {
2812           XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
2813             entry = XtCreateManagedWidget(name, smeBSBObjectClass,
2814                                           currentMenu, args, j+1);
2815             XtAddCallback(entry, XtNcallback,
2816                           (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
2817                           (caddr_t) action);
2818             menuWidget[nrOfMenuItems] = entry;
2819         }
2820 }
2821
2822 void
2823 CreateMenuButton (char *name, Menu *mb)
2824 {   // create menu button on main bar, and shell for pull-down list
2825     int i, j;
2826     Arg args[16];
2827     Dimension w;
2828
2829         j = 0;
2830         XtSetArg(args[j], XtNmenuName, XtNewString(name));  j++;
2831         XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name)));  j++;
2832         XtSetArg(args[j], XtNborderWidth, 0);                   j++;
2833         mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2834                                        mainBar, args, j);
2835     currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2836                               mainBar, NULL, 0);
2837         j = 0;
2838         XtSetArg(args[j], XtNwidth, &w);                   j++;
2839         XtGetValues(mb->subMenu, args, j);
2840         wtot += mb->textWidth = widths[nr++] = w;
2841 }
2842
2843 Widget
2844 CreateMenuBar (Menu *mb, int boardWidth)
2845 {
2846     int i, j;
2847     Arg args[16];
2848     char menuName[MSG_SIZ];
2849     Dimension w;
2850     Menu *ma = mb;
2851
2852     // create bar itself
2853     j = 0;
2854     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
2855     XtSetArg(args[j], XtNvSpace, 0);                        j++;
2856     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
2857     mainBar = XtCreateWidget("menuBar", boxWidgetClass,
2858                              formWidget, args, j);
2859
2860     CreateMainMenus(mb); // put menus in bar according to description in back-end
2861
2862     // size buttons to make menu bar fit, clipping menu names where necessary
2863     while(wtot > boardWidth - 40) {
2864         int wmax=0, imax=0;
2865         for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
2866         widths[imax]--;
2867         wtot--;
2868     }
2869     for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
2870         j = 0;
2871         XtSetArg(args[j], XtNwidth, widths[i]);                   j++;
2872         XtSetValues(ma[i].subMenu, args, j);
2873     }
2874
2875     return mainBar;
2876 }
2877
2878 Widget
2879 CreateButtonBar (MenuItem *mi)
2880 {
2881     int j;
2882     Widget button, buttonBar;
2883     Arg args[16];
2884
2885     j = 0;
2886     XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2887     if (tinyLayout) {
2888         XtSetArg(args[j], XtNhSpace, 0); j++;
2889     }
2890     XtSetArg(args[j], XtNborderWidth, 0); j++;
2891     XtSetArg(args[j], XtNvSpace, 0);                        j++;
2892     buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
2893                                formWidget, args, j);
2894
2895     while (mi->string != NULL) {
2896         j = 0;
2897         if (tinyLayout) {
2898             XtSetArg(args[j], XtNinternalWidth, 2); j++;
2899             XtSetArg(args[j], XtNborderWidth, 0); j++;
2900         }
2901       XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
2902         button = XtCreateManagedWidget(mi->string, commandWidgetClass,
2903                                        buttonBar, args, j);
2904         XtAddCallback(button, XtNcallback,
2905                       (XtCallbackProc) MenuBarSelect,
2906                       (caddr_t) mi->proc);
2907         mi++;
2908     }
2909     return buttonBar;
2910 }
2911
2912 Widget
2913 CreatePieceMenu (char *name, int color)
2914 {
2915     int i;
2916     Widget entry, menu;
2917     Arg args[16];
2918     ChessSquare selection;
2919
2920     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2921                               boardWidget, args, 0);
2922
2923     for (i = 0; i < PIECE_MENU_SIZE; i++) {
2924         String item = pieceMenuStrings[color][i];
2925
2926         if (strcmp(item, "----") == 0) {
2927             entry = XtCreateManagedWidget(item, smeLineObjectClass,
2928                                           menu, NULL, 0);
2929         } else {
2930           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2931             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2932                                 menu, args, 1);
2933             selection = pieceMenuTranslation[color][i];
2934             XtAddCallback(entry, XtNcallback,
2935                           (XtCallbackProc) PieceMenuSelect,
2936                           (caddr_t) selection);
2937             if (selection == WhitePawn || selection == BlackPawn) {
2938                 XtSetArg(args[0], XtNpopupOnEntry, entry);
2939                 XtSetValues(menu, args, 1);
2940             }
2941         }
2942     }
2943     return menu;
2944 }
2945
2946 void
2947 CreatePieceMenus ()
2948 {
2949     int i;
2950     Widget entry;
2951     Arg args[16];
2952     ChessSquare selection;
2953
2954     whitePieceMenu = CreatePieceMenu("menuW", 0);
2955     blackPieceMenu = CreatePieceMenu("menuB", 1);
2956
2957     if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
2958     XtRegisterGrabAction(PieceMenuPopup, True,
2959                          (unsigned)(ButtonPressMask|ButtonReleaseMask),
2960                          GrabModeAsync, GrabModeAsync);
2961
2962     XtSetArg(args[0], XtNlabel, _("Drop"));
2963     dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2964                                   boardWidget, args, 1);
2965     for (i = 0; i < DROP_MENU_SIZE; i++) {
2966         String item = dropMenuStrings[i];
2967
2968         if (strcmp(item, "----") == 0) {
2969             entry = XtCreateManagedWidget(item, smeLineObjectClass,
2970                                           dropMenu, NULL, 0);
2971         } else {
2972           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2973             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2974                                 dropMenu, args, 1);
2975             selection = dropMenuTranslation[i];
2976             XtAddCallback(entry, XtNcallback,
2977                           (XtCallbackProc) DropMenuSelect,
2978                           (caddr_t) selection);
2979         }
2980     }
2981 }
2982
2983 void
2984 SetupDropMenu ()
2985 {
2986     int i, j, count;
2987     char label[32];
2988     Arg args[16];
2989     Widget entry;
2990     char* p;
2991
2992     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2993         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2994         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2995                    dmEnables[i].piece);
2996         XtSetSensitive(entry, p != NULL || !appData.testLegality
2997                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2998                                        && !appData.icsActive));
2999         count = 0;
3000         while (p && *p++ == dmEnables[i].piece) count++;
3001         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
3002         j = 0;
3003         XtSetArg(args[j], XtNlabel, label); j++;
3004         XtSetValues(entry, args, j);
3005     }
3006 }
3007
3008 void
3009 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3010 {
3011     String whichMenu; int menuNr = -2;
3012     shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3013     if (event->type == ButtonRelease)
3014         menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3015     else if (event->type == ButtonPress)
3016         menuNr = RightClick(Press,   event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3017     switch(menuNr) {
3018       case 0: whichMenu = params[0]; break;
3019       case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3020       case 2:
3021       case -1: ErrorPopDown();
3022       default: return;
3023     }
3024     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3025 }
3026
3027 static void
3028 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3029 {
3030     if (pmFromX < 0 || pmFromY < 0) return;
3031     EditPositionMenuEvent(piece, pmFromX, pmFromY);
3032 }
3033
3034 static void
3035 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3036 {
3037     if (pmFromX < 0 || pmFromY < 0) return;
3038     DropMenuEvent(piece, pmFromX, pmFromY);
3039 }
3040
3041 void
3042 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3043 {
3044     shiftKey = prms[0][0] & 1;
3045     ClockClick(0);
3046 }
3047
3048 void
3049 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3050 {
3051     shiftKey = prms[0][0] & 1;
3052     ClockClick(1);
3053 }
3054
3055
3056 static void
3057 do_flash_delay (unsigned long msec)
3058 {
3059     TimeDelay(msec);
3060 }
3061
3062 void
3063 DrawBorder (int x, int y, int type)
3064 {
3065     GC gc = lineGC;
3066
3067     if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3068
3069     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3070                    squareSize+lineGap, squareSize+lineGap);
3071 }
3072
3073 static int
3074 CutOutSquare (int x, int y, int *x0, int *y0, int  kind)
3075 {
3076     int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3077     int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3078     *x0 = 0; *y0 = 0;
3079     if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3080     if(textureW[kind] < W*squareSize)
3081         *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3082     else
3083         *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3084     if(textureH[kind] < H*squareSize)
3085         *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3086     else
3087         *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3088     return 1;
3089 }
3090
3091 static void
3092 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3093 {   // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3094     int x0, y0;
3095     if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3096         XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3097                   squareSize, squareSize, x*fac, y*fac);
3098     } else
3099     if (useImages && useImageSqs) {
3100         Pixmap pm;
3101         switch (color) {
3102           case 1: /* light */
3103             pm = xpmLightSquare;
3104             break;
3105           case 0: /* dark */
3106             pm = xpmDarkSquare;
3107             break;
3108           case 2: /* neutral */
3109           default:
3110             pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
3111             break;
3112         }
3113         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3114                   squareSize, squareSize, x*fac, y*fac);
3115     } else {
3116         GC gc;
3117         switch (color) {
3118           case 1: /* light */
3119             gc = lightSquareGC;
3120             break;
3121           case 0: /* dark */
3122             gc = darkSquareGC;
3123             break;
3124           case 2: /* neutral */
3125           default:
3126             gc = lineGC;
3127             break;
3128         }
3129         XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3130     }
3131 }
3132
3133 /*
3134    I split out the routines to draw a piece so that I could
3135    make a generic flash routine.
3136 */
3137 static void
3138 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3139 {
3140     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3141     switch (square_color) {
3142       case 1: /* light */
3143       case 2: /* neutral */
3144       default:
3145         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3146                   ? *pieceToOutline(piece)
3147                   : *pieceToSolid(piece),
3148                   dest, bwPieceGC, 0, 0,
3149                   squareSize, squareSize, x, y);
3150         break;
3151       case 0: /* dark */
3152         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3153                   ? *pieceToSolid(piece)
3154                   : *pieceToOutline(piece),
3155                   dest, wbPieceGC, 0, 0,
3156                   squareSize, squareSize, x, y);
3157         break;
3158     }
3159 }
3160
3161 static void
3162 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3163 {
3164     switch (square_color) {
3165       case 1: /* light */
3166       case 2: /* neutral */
3167       default:
3168         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3169                    ? *pieceToOutline(piece)
3170                    : *pieceToSolid(piece),
3171                    dest, bwPieceGC, 0, 0,
3172                    squareSize, squareSize, x, y, 1);
3173         break;
3174       case 0: /* dark */
3175         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3176                    ? *pieceToSolid(piece)
3177                    : *pieceToOutline(piece),
3178                    dest, wbPieceGC, 0, 0,
3179                    squareSize, squareSize, x, y, 1);
3180         break;
3181     }
3182 }
3183
3184 static void
3185 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3186 {
3187     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3188     switch (square_color) {
3189       case 1: /* light */
3190         XCopyPlane(xDisplay, *pieceToSolid(piece),
3191                    dest, (int) piece < (int) BlackPawn
3192                    ? wlPieceGC : blPieceGC, 0, 0,
3193                    squareSize, squareSize, x, y, 1);
3194         break;
3195       case 0: /* dark */
3196         XCopyPlane(xDisplay, *pieceToSolid(piece),
3197                    dest, (int) piece < (int) BlackPawn
3198                    ? wdPieceGC : bdPieceGC, 0, 0,
3199                    squareSize, squareSize, x, y, 1);
3200         break;
3201       case 2: /* neutral */
3202       default:
3203         break; // should never contain pieces
3204     }
3205 }
3206
3207 static void
3208 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3209 {
3210     int kind, p = piece;
3211
3212     switch (square_color) {
3213       case 1: /* light */
3214       case 2: /* neutral */
3215       default:
3216         if ((int)piece < (int) BlackPawn) {
3217             kind = 0;
3218         } else {
3219             kind = 2;
3220             piece -= BlackPawn;
3221         }
3222         break;
3223       case 0: /* dark */
3224         if ((int)piece < (int) BlackPawn) {
3225             kind = 1;
3226         } else {
3227             kind = 3;
3228             piece -= BlackPawn;
3229         }
3230         break;
3231     }
3232     if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3233     if(useTexture & square_color+1) {
3234         BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3235         XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3236         XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3237         XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3238         XSetClipMask(xDisplay, wlPieceGC, None);
3239         XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3240     } else
3241     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3242               dest, wlPieceGC, 0, 0,
3243               squareSize, squareSize, x, y);
3244 }
3245
3246 typedef void (*DrawFunc)();
3247
3248 DrawFunc
3249 ChooseDrawFunc ()
3250 {
3251     if (appData.monoMode) {
3252         if (DefaultDepth(xDisplay, xScreen) == 1) {
3253             return monoDrawPiece_1bit;
3254         } else {
3255             return monoDrawPiece;
3256         }
3257     } else {
3258         if (useImages)
3259           return colorDrawPieceImage;
3260         else
3261           return colorDrawPiece;
3262     }
3263 }
3264
3265 void
3266 DrawDot (int marker, int x, int y, int r)
3267 {
3268         if(appData.monoMode) {
3269             XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3270                     x, y, r, r, 0, 64*360);
3271             XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3272                     x, y, r, r, 0, 64*360);
3273         } else
3274         XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3275                     x, y, r, r, 0, 64*360);
3276 }
3277
3278 void
3279 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3280 {   // basic front-end board-draw function: takes care of everything that can be in square:
3281     // piece, background, coordinate/count, marker dot
3282     int direction, font_ascent, font_descent;
3283     XCharStruct overall;
3284     DrawFunc drawfunc;
3285
3286     if (piece == EmptySquare) {
3287         BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3288     } else {
3289         drawfunc = ChooseDrawFunc();
3290         drawfunc(piece, square_color, x, y, xBoardWindow);
3291     }
3292
3293     if(align) { // square carries inscription (coord or piece count)
3294         int xx = x, yy = y;
3295         GC hGC = align < 3 ? coordGC : countGC;
3296         // first calculate where it goes
3297         XTextExtents(countFontStruct, string, 1, &direction,
3298                          &font_ascent, &font_descent, &overall);
3299         if (align == 1) {
3300             xx += squareSize - overall.width - 2;
3301             yy += squareSize - font_descent - 1;
3302         } else if (align == 2) {
3303             xx += 2, yy += font_ascent + 1;
3304         } else if (align == 3) {
3305             xx += squareSize - overall.width - 2;
3306             yy += font_ascent + 1;
3307         } else if (align == 4) {
3308             xx += 2, yy += font_ascent + 1;
3309         }
3310         // then draw it
3311         if (appData.monoMode) {
3312             XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3313         } else {
3314             XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3315         }
3316     }
3317
3318     if(marker) { // print fat marker dot, if requested
3319         DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3320     }
3321 }
3322
3323 void
3324 FlashDelay (int flash_delay)
3325 {
3326         XSync(xDisplay, False);
3327         if(flash_delay) do_flash_delay(flash_delay);
3328 }
3329
3330 double
3331 Fraction (int x, int start, int stop)
3332 {
3333    double f = ((double) x - start)/(stop - start);
3334    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3335    return f;
3336 }
3337
3338 static WindowPlacement wpNew;
3339
3340 void
3341 CoDrag (Widget sh, WindowPlacement *wp)
3342 {
3343     Arg args[16];
3344     int j=0, touch=0, fudge = 2;
3345     GetActualPlacement(sh, wp);
3346     if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x)         < fudge) touch = 1; else // right touch
3347     if(abs(wp->x + wp->width + 2*frameX - wpMain.x)            < fudge) touch = 2; else // left touch
3348     if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3349     if(abs(wp->y + wp->height + frameX + frameY - wpMain.y)    < fudge) touch = 4;      // top touch
3350     if(!touch ) return; // only windows that touch co-move
3351     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3352         int heightInc = wpNew.height - wpMain.height;
3353         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3354         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3355         wp->y += fracTop * heightInc;
3356         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3357         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3358     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3359         int widthInc = wpNew.width - wpMain.width;
3360         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3361         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3362         wp->y += fracLeft * widthInc;
3363         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3364         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3365     }
3366     wp->x += wpNew.x - wpMain.x;
3367     wp->y += wpNew.y - wpMain.y;
3368     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3369     if(touch == 3) wp->y += wpNew.height - wpMain.height;
3370     XtSetArg(args[j], XtNx, wp->x); j++;
3371     XtSetArg(args[j], XtNy, wp->y); j++;
3372     XtSetValues(sh, args, j);
3373 }
3374
3375 static XtIntervalId delayedDragID = 0;
3376
3377 void
3378 DragProc ()
3379 {
3380         GetActualPlacement(shellWidget, &wpNew);
3381         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3382            wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3383             return; // false alarm
3384         if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
3385         if(MoveHistoryIsUp()) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3386         if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3387         if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
3388         wpMain = wpNew;
3389         DrawPosition(True, NULL);
3390         delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3391 }
3392
3393
3394 void
3395 DelayedDrag ()
3396 {
3397     if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3398     delayedDragID =
3399       XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3400 }
3401
3402 /* Why is this needed on some versions of X? */
3403 void
3404 EventProc (Widget widget, caddr_t unused, XEvent *event)
3405 {
3406     if (!XtIsRealized(widget))
3407       return;
3408     switch (event->type) {
3409       case ConfigureNotify: // main window is being dragged: drag attached windows with it
3410         if(appData.useStickyWindows)
3411             DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3412         break;
3413       case Expose:
3414         if (event->xexpose.count > 0) return;  /* no clipping is done */
3415         DrawPosition(True, NULL);
3416         if(twoBoards) { // [HGM] dual: draw other board in other orientation
3417             flipView = !flipView; partnerUp = !partnerUp;
3418             DrawPosition(True, NULL);
3419             flipView = !flipView; partnerUp = !partnerUp;
3420         }
3421         break;
3422       case MotionNotify:
3423         if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3424       default:
3425         return;
3426     }
3427 }
3428 /* end why */
3429
3430 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3431 void
3432 DrawSeekAxis (int x, int y, int xTo, int yTo)
3433 {
3434       XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3435 }
3436
3437 void
3438 DrawSeekBackground (int left, int top, int right, int bottom)
3439 {
3440     XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3441 }
3442
3443 void
3444 DrawSeekText (char *buf, int x, int y)
3445 {
3446     XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3447 }
3448
3449 void
3450 DrawSeekDot (int x, int y, int colorNr)
3451 {
3452     int square = colorNr & 0x80;
3453     GC color;
3454     colorNr &= 0x7F;
3455     color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3456     if(square)
3457         XFillRectangle(xDisplay, xBoardWindow, color,
3458                 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3459     else
3460         XFillArc(xDisplay, xBoardWindow, color,
3461                 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3462 }
3463
3464 void
3465 DrawGrid (int second)
3466 {
3467           XDrawSegments(xDisplay, xBoardWindow, lineGC,
3468                         second ? secondSegments : // [HGM] dual
3469                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3470 }
3471
3472
3473 /*
3474  * event handler for redrawing the board
3475  */
3476 void
3477 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3478 {
3479     DrawPosition(True, NULL);
3480 }
3481
3482
3483 /*
3484  * event handler for parsing user moves
3485  */
3486 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3487 //       way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3488 //       it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3489 //       should be made to use the new way, of calling UserMoveTest early  to determine the legality of the
3490 //       move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3491 //       and at the end FinishMove() to perform the move after optional promotion popups.
3492 //       For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3493 void
3494 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3495 {
3496     if (w != boardWidget || errorExitStatus != -1) return;
3497     if(nprms) shiftKey = !strcmp(prms[0], "1");
3498
3499     if (shellUp[PromoDlg]) { // [HGM] is this still needed?
3500         if (event->type == ButtonPress) {
3501             PopDown(PromoDlg);
3502             ClearHighlights();
3503             fromX = fromY = -1;
3504         } else {
3505             return;
3506         }
3507     }
3508
3509     // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3510     if(event->type == ButtonPress)   LeftClick(Press,   event->xbutton.x, event->xbutton.y);
3511     if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3512 }
3513
3514 void
3515 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3516 {
3517     if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3518     DragPieceMove(event->xmotion.x, event->xmotion.y);
3519 }
3520
3521 void
3522 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3523 {   // [HGM] pv: walk PV
3524     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3525 }
3526
3527 static int savedIndex;  /* gross that this is global */
3528
3529 void
3530 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3531 {
3532         String val;
3533         XawTextPosition index, dummy;
3534         Arg arg;
3535
3536         XawTextGetSelectionPos(w, &index, &dummy);
3537         XtSetArg(arg, XtNstring, &val);
3538         XtGetValues(w, &arg, 1);
3539         ReplaceComment(savedIndex, val);
3540         if(savedIndex != currentMove) ToNrEvent(savedIndex);
3541         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3542 }
3543
3544 void
3545 EditCommentPopUp (int index, char *title, char *text)
3546 {
3547     savedIndex = index;
3548     if (text == NULL) text = "";
3549     NewCommentPopup(title, text, index);
3550 }
3551
3552 void
3553 CommentPopUp (char *title, char *text)
3554 {
3555     savedIndex = currentMove; // [HGM] vari
3556     NewCommentPopup(title, text, currentMove);
3557 }
3558
3559 void
3560 CommentPopDown ()
3561 {
3562     PopDown(CommentDlg);
3563 }
3564
3565 static char *openName;
3566 FILE *openFP;
3567
3568 void
3569 DelayedLoad ()
3570 {
3571   (void) (*fileProc)(openFP, 0, openName);
3572 }
3573
3574 void
3575 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3576 {
3577     fileProc = proc;            /* I can't see a way not */
3578     fileOpenMode = openMode;    /*   to use globals here */
3579     {   // [HGM] use file-selector dialog stolen from Ghostview
3580         int index; // this is not supported yet
3581         if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3582                            (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3583           // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3584           ScheduleDelayedEvent(&DelayedLoad, 50);
3585     }
3586 }
3587
3588
3589 /* Disable all user input other than deleting the window */
3590 static int frozen = 0;
3591
3592 void
3593 FreezeUI ()
3594 {
3595   if (frozen) return;
3596   /* Grab by a widget that doesn't accept input */
3597   XtAddGrab(messageWidget, TRUE, FALSE);
3598   frozen = 1;
3599 }
3600
3601 /* Undo a FreezeUI */
3602 void
3603 ThawUI ()
3604 {
3605   if (!frozen) return;
3606   XtRemoveGrab(messageWidget);
3607   frozen = 0;
3608 }
3609
3610 void
3611 ModeHighlight ()
3612 {
3613     Arg args[16];
3614     static int oldPausing = FALSE;
3615     static GameMode oldmode = (GameMode) -1;
3616     char *wname;
3617
3618     if (!boardWidget || !XtIsRealized(boardWidget)) return;
3619
3620     if (pausing != oldPausing) {
3621         oldPausing = pausing;
3622         MarkMenuItem("Pause", pausing);
3623
3624         if (appData.showButtonBar) {
3625           /* Always toggle, don't set.  Previous code messes up when
3626              invoked while the button is pressed, as releasing it
3627              toggles the state again. */
3628           {
3629             Pixel oldbg, oldfg;
3630             XtSetArg(args[0], XtNbackground, &oldbg);
3631             XtSetArg(args[1], XtNforeground, &oldfg);
3632             XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
3633                         args, 2);
3634             XtSetArg(args[0], XtNbackground, oldfg);
3635             XtSetArg(args[1], XtNforeground, oldbg);
3636           }
3637           XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
3638         }
3639     }
3640
3641     wname = ModeToWidgetName(oldmode);
3642     if (wname != NULL) {
3643         MarkMenuItem(wname, False);
3644     }
3645     wname = ModeToWidgetName(gameMode);
3646     if (wname != NULL) {
3647         MarkMenuItem(wname, True);
3648     }
3649     oldmode = gameMode;
3650     MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
3651
3652     /* Maybe all the enables should be handled here, not just this one */
3653     EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
3654 }
3655
3656
3657 /*
3658  * Button/menu procedures
3659  */
3660 int
3661 LoadGamePopUp (FILE *f, int gameNumber, char *title)
3662 {
3663     cmailMsgLoaded = FALSE;
3664     if (gameNumber == 0) {
3665         int error = GameListBuild(f);
3666         if (error) {
3667             DisplayError(_("Cannot build game list"), error);
3668         } else if (!ListEmpty(&gameList) &&
3669                    ((ListGame *) gameList.tailPred)->number > 1) {
3670             GameListPopUp(f, title);
3671             return TRUE;
3672         }
3673         GameListDestroy();
3674         gameNumber = 1;
3675     }
3676     return LoadGame(f, gameNumber, title, FALSE);
3677 }
3678
3679 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3680 char *selected_fen_position=NULL;
3681
3682 Boolean
3683 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3684                        Atom *type_return, XtPointer *value_return,
3685                        unsigned long *length_return, int *format_return)
3686 {
3687   char *selection_tmp;
3688
3689 //  if (!selected_fen_position) return False; /* should never happen */
3690   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3691    if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3692     FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3693     long len;
3694     size_t count;
3695     if (f == NULL) return False;
3696     fseek(f, 0, 2);
3697     len = ftell(f);
3698     rewind(f);
3699     selection_tmp = XtMalloc(len + 1);
3700     count = fread(selection_tmp, 1, len, f);
3701     fclose(f);
3702     if (len != count) {
3703       XtFree(selection_tmp);
3704       return False;
3705     }
3706     selection_tmp[len] = NULLCHAR;
3707    } else {
3708     /* note: since no XtSelectionDoneProc was registered, Xt will
3709      * automatically call XtFree on the value returned.  So have to
3710      * make a copy of it allocated with XtMalloc */
3711     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3712     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3713    }
3714
3715     *value_return=selection_tmp;
3716     *length_return=strlen(selection_tmp);
3717     *type_return=*target;
3718     *format_return = 8; /* bits per byte */
3719     return True;
3720   } else if (*target == XA_TARGETS(xDisplay)) {
3721     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3722     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3723     targets_tmp[1] = XA_STRING;
3724     *value_return = targets_tmp;
3725     *type_return = XA_ATOM;
3726     *length_return = 2;
3727 #if 0
3728     // This code leads to a read of value_return out of bounds on 64-bit systems.
3729     // Other code which I have seen always sets *format_return to 32 independent of
3730     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3731     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3732     *format_return = 8 * sizeof(Atom);
3733     if (*format_return > 32) {
3734       *length_return *= *format_return / 32;
3735       *format_return = 32;
3736     }
3737 #else
3738     *format_return = 32;
3739 #endif
3740     return True;
3741   } else {
3742     return False;
3743   }
3744 }
3745
3746 /* note: when called from menu all parameters are NULL, so no clue what the
3747  * Widget which was clicked on was, or what the click event was
3748  */
3749 void
3750 CopySomething (char *src)
3751 {
3752     selected_fen_position = src;
3753     /*
3754      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3755      * have a notion of a position that is selected but not copied.
3756      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3757      */
3758     XtOwnSelection(menuBarWidget, XA_PRIMARY,
3759                    CurrentTime,
3760                    SendPositionSelection,
3761                    NULL/* lose_ownership_proc */ ,
3762                    NULL/* transfer_done_proc */);
3763     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3764                    CurrentTime,
3765                    SendPositionSelection,
3766                    NULL/* lose_ownership_proc */ ,
3767                    NULL/* transfer_done_proc */);
3768 }
3769
3770 /* function called when the data to Paste is ready */
3771 static void
3772 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3773                  Atom *type, XtPointer value, unsigned long *len, int *format)
3774 {
3775   char *fenstr=value;
3776   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3777   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3778   EditPositionPasteFEN(fenstr);
3779   XtFree(value);
3780 }
3781
3782 /* called when Paste Position button is pressed,
3783  * all parameters will be NULL */
3784 void
3785 PastePositionProc ()
3786 {
3787     XtGetSelectionValue(menuBarWidget,
3788       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3789       /* (XtSelectionCallbackProc) */ PastePositionCB,
3790       NULL, /* client_data passed to PastePositionCB */
3791
3792       /* better to use the time field from the event that triggered the
3793        * call to this function, but that isn't trivial to get
3794        */
3795       CurrentTime
3796     );
3797     return;
3798 }
3799
3800 /* note: when called from menu all parameters are NULL, so no clue what the
3801  * Widget which was clicked on was, or what the click event was
3802  */
3803 /* function called when the data to Paste is ready */
3804 static void
3805 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3806              Atom *type, XtPointer value, unsigned long *len, int *format)
3807 {
3808   FILE* f;
3809   if (value == NULL || *len == 0) {
3810     return; /* nothing had been selected to copy */
3811   }
3812   f = fopen(gamePasteFilename, "w");
3813   if (f == NULL) {
3814     DisplayError(_("Can't open temp file"), errno);
3815     return;
3816   }
3817   fwrite(value, 1, *len, f);
3818   fclose(f);
3819   XtFree(value);
3820   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3821 }
3822
3823 /* called when Paste Game button is pressed,
3824  * all parameters will be NULL */
3825 void
3826 PasteGameProc ()
3827 {
3828     XtGetSelectionValue(menuBarWidget,
3829       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3830       /* (XtSelectionCallbackProc) */ PasteGameCB,
3831       NULL, /* client_data passed to PasteGameCB */
3832
3833       /* better to use the time field from the event that triggered the
3834        * call to this function, but that isn't trivial to get
3835        */
3836       CurrentTime
3837     );
3838     return;
3839 }
3840
3841
3842 void
3843 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3844 {
3845     QuitProc();
3846 }
3847
3848 int
3849 ShiftKeys ()
3850 {   // bassic primitive for determining if modifier keys are pressed
3851     long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3852     char keys[32];
3853     int i,j,  k=0;
3854     XQueryKeymap(xDisplay,keys);
3855     for(i=0; i<6; i++) {
3856         k <<= 1;
3857         j = XKeysymToKeycode(xDisplay, codes[i]);
3858         k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3859     }
3860     return k;
3861 }
3862
3863 static void
3864 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3865 {
3866     char buf[10];
3867     KeySym sym;
3868     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3869     if ( n == 1 && *buf >= 32 // printable
3870          && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3871         ) BoxAutoPopUp (buf);
3872 }
3873
3874 static void
3875 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3876 {   // [HGM] input: let up-arrow recall previous line from history
3877     IcsKey(1);
3878 }
3879
3880 static void
3881 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3882 {   // [HGM] input: let down-arrow recall next line from history
3883     IcsKey(-1);
3884 }
3885
3886 static void
3887 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3888 {
3889     IcsKey(0);
3890 }
3891
3892 void
3893 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3894 {
3895         if (!TempBackwardActive) {
3896                 TempBackwardActive = True;
3897                 BackwardEvent();
3898         }
3899 }
3900
3901 void
3902 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3903 {
3904         /* Check to see if triggered by a key release event for a repeating key.
3905          * If so the next queued event will be a key press of the same key at the same time */
3906         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3907                 XEvent next;
3908                 XPeekEvent(xDisplay, &next);
3909                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3910                         next.xkey.keycode == event->xkey.keycode)
3911                                 return;
3912         }
3913     ForwardEvent();
3914         TempBackwardActive = False;
3915 }
3916
3917 void
3918 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3919 {   // called as key binding
3920     char buf[MSG_SIZ];
3921     String name;
3922     if (nprms && *nprms > 0)
3923       name = prms[0];
3924     else
3925       name = "xboard";
3926     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3927     system(buf);
3928 }
3929
3930 void
3931 DisplayMessage (char *message, char *extMessage)
3932 {
3933   /* display a message in the message widget */
3934
3935   char buf[MSG_SIZ];
3936   Arg arg;
3937
3938   if (extMessage)
3939     {
3940       if (*message)
3941         {
3942           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
3943           message = buf;
3944         }
3945       else
3946         {
3947           message = extMessage;
3948         };
3949     };
3950
3951     safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
3952
3953   /* need to test if messageWidget already exists, since this function
3954      can also be called during the startup, if for example a Xresource
3955      is not set up correctly */
3956   if(messageWidget)
3957     {
3958       XtSetArg(arg, XtNlabel, message);
3959       XtSetValues(messageWidget, &arg, 1);
3960     };
3961
3962   return;
3963 }
3964
3965 void
3966 SetWindowTitle (char *text, char *title, char *icon)
3967 {
3968     Arg args[16];
3969     int i;
3970     if (appData.titleInWindow) {
3971         i = 0;
3972         XtSetArg(args[i], XtNlabel, text);   i++;
3973         XtSetValues(titleWidget, args, i);
3974     }
3975     i = 0;
3976     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
3977     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
3978     XtSetValues(shellWidget, args, i);
3979     XSync(xDisplay, False);
3980 }
3981
3982
3983 static int
3984 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3985 {
3986     return 0;
3987 }
3988
3989 void
3990 DisplayIcsInteractionTitle (String message)
3991 {
3992   if (oldICSInteractionTitle == NULL) {
3993     /* Magic to find the old window title, adapted from vim */
3994     char *wina = getenv("WINDOWID");
3995     if (wina != NULL) {
3996       Window win = (Window) atoi(wina);
3997       Window root, parent, *children;
3998       unsigned int nchildren;
3999       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4000       for (;;) {
4001         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4002         if (!XQueryTree(xDisplay, win, &root, &parent,
4003                         &children, &nchildren)) break;
4004         if (children) XFree((void *)children);
4005         if (parent == root || parent == 0) break;
4006         win = parent;
4007       }
4008       XSetErrorHandler(oldHandler);
4009     }
4010     if (oldICSInteractionTitle == NULL) {
4011       oldICSInteractionTitle = "xterm";
4012     }
4013   }
4014   printf("\033]0;%s\007", message);
4015   fflush(stdout);
4016 }
4017
4018
4019 XtIntervalId delayedEventTimerXID = 0;
4020 DelayedEventCallback delayedEventCallback = 0;
4021
4022 void
4023 FireDelayedEvent ()
4024 {
4025     delayedEventTimerXID = 0;
4026     delayedEventCallback();
4027 }
4028
4029 void
4030 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
4031 {
4032     if(delayedEventTimerXID && delayedEventCallback == cb)
4033         // [HGM] alive: replace, rather than add or flush identical event
4034         XtRemoveTimeOut(delayedEventTimerXID);
4035     delayedEventCallback = cb;
4036     delayedEventTimerXID =
4037       XtAppAddTimeOut(appContext, millisec,
4038                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
4039 }
4040
4041 DelayedEventCallback
4042 GetDelayedEvent ()
4043 {
4044   if (delayedEventTimerXID) {
4045     return delayedEventCallback;
4046   } else {
4047     return NULL;
4048   }
4049 }
4050
4051 void
4052 CancelDelayedEvent ()
4053 {
4054   if (delayedEventTimerXID) {
4055     XtRemoveTimeOut(delayedEventTimerXID);
4056     delayedEventTimerXID = 0;
4057   }
4058 }
4059
4060 XtIntervalId loadGameTimerXID = 0;
4061
4062 int
4063 LoadGameTimerRunning ()
4064 {
4065     return loadGameTimerXID != 0;
4066 }
4067
4068 int
4069 StopLoadGameTimer ()
4070 {
4071     if (loadGameTimerXID != 0) {
4072         XtRemoveTimeOut(loadGameTimerXID);
4073         loadGameTimerXID = 0;
4074         return TRUE;
4075     } else {
4076         return FALSE;
4077     }
4078 }
4079
4080 void
4081 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
4082 {
4083     loadGameTimerXID = 0;
4084     AutoPlayGameLoop();
4085 }
4086
4087 void
4088 StartLoadGameTimer (long millisec)
4089 {
4090     loadGameTimerXID =
4091       XtAppAddTimeOut(appContext, millisec,
4092                       (XtTimerCallbackProc) LoadGameTimerCallback,
4093                       (XtPointer) 0);
4094 }
4095
4096 XtIntervalId analysisClockXID = 0;
4097
4098 void
4099 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
4100 {
4101     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4102          || appData.icsEngineAnalyze) { // [DM]
4103         AnalysisPeriodicEvent(0);
4104         StartAnalysisClock();
4105     }
4106 }
4107
4108 void
4109 StartAnalysisClock ()
4110 {
4111     analysisClockXID =
4112       XtAppAddTimeOut(appContext, 2000,
4113                       (XtTimerCallbackProc) AnalysisClockCallback,
4114                       (XtPointer) 0);
4115 }
4116
4117 XtIntervalId clockTimerXID = 0;
4118
4119 int
4120 ClockTimerRunning ()
4121 {
4122     return clockTimerXID != 0;
4123 }
4124
4125 int
4126 StopClockTimer ()
4127 {
4128     if (clockTimerXID != 0) {
4129         XtRemoveTimeOut(clockTimerXID);
4130         clockTimerXID = 0;
4131         return TRUE;
4132     } else {
4133         return FALSE;
4134     }
4135 }
4136
4137 void
4138 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
4139 {
4140     clockTimerXID = 0;
4141     DecrementClocks();
4142 }
4143
4144 void
4145 StartClockTimer (long millisec)
4146 {
4147     clockTimerXID =
4148       XtAppAddTimeOut(appContext, millisec,
4149                       (XtTimerCallbackProc) ClockTimerCallback,
4150                       (XtPointer) 0);
4151 }
4152
4153 void
4154 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
4155 {
4156     char buf[MSG_SIZ];
4157     Arg args[16];
4158
4159     /* check for low time warning */
4160     Pixel foregroundOrWarningColor = timerForegroundPixel;
4161
4162     if (timer > 0 &&
4163         appData.lowTimeWarning &&
4164         (timer / 1000) < appData.icsAlarmTime)
4165       foregroundOrWarningColor = lowTimeWarningColor;
4166
4167     if (appData.clockMode) {
4168       snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
4169       XtSetArg(args[0], XtNlabel, buf);
4170     } else {
4171       snprintf(buf, MSG_SIZ, "%s  ", color);
4172       XtSetArg(args[0], XtNlabel, buf);
4173     }
4174
4175     if (highlight) {
4176
4177         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
4178         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
4179     } else {
4180         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
4181         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
4182     }
4183
4184     XtSetValues(w, args, 3);
4185 }
4186
4187 void
4188 DisplayWhiteClock (long timeRemaining, int highlight)
4189 {
4190     Arg args[16];
4191
4192     if(appData.noGUI) return;
4193     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
4194     if (highlight && iconPixmap == bIconPixmap) {
4195         iconPixmap = wIconPixmap;
4196         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4197         XtSetValues(shellWidget, args, 1);
4198     }
4199 }
4200
4201 void
4202 DisplayBlackClock (long timeRemaining, int highlight)
4203 {
4204     Arg args[16];
4205
4206     if(appData.noGUI) return;
4207     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
4208     if (highlight && iconPixmap == wIconPixmap) {
4209         iconPixmap = bIconPixmap;
4210         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4211         XtSetValues(shellWidget, args, 1);
4212     }
4213 }
4214
4215 void
4216 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
4217 {
4218     InputSource *is = (InputSource *) closure;
4219     int count;
4220     int error;
4221     char *p, *q;
4222
4223     if (is->lineByLine) {
4224         count = read(is->fd, is->unused,
4225                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
4226         if (count <= 0) {
4227             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
4228             return;
4229         }
4230         is->unused += count;
4231         p = is->buf;
4232         while (p < is->unused) {
4233             q = memchr(p, '\n', is->unused - p);
4234             if (q == NULL) break;
4235             q++;
4236             (is->func)(is, is->closure, p, q - p, 0);
4237             p = q;
4238         }
4239         q = is->buf;
4240         while (p < is->unused) {
4241             *q++ = *p++;
4242         }
4243         is->unused = q;
4244     } else {
4245         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
4246         if (count == -1)
4247           error = errno;
4248         else
4249           error = 0;
4250         (is->func)(is, is->closure, is->buf, count, error);
4251     }
4252 }
4253
4254 InputSourceRef
4255 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
4256 {
4257     InputSource *is;
4258     ChildProc *cp = (ChildProc *) pr;
4259
4260     is = (InputSource *) calloc(1, sizeof(InputSource));
4261     is->lineByLine = lineByLine;
4262     is->func = func;
4263     if (pr == NoProc) {
4264         is->kind = CPReal;
4265         is->fd = fileno(stdin);
4266     } else {
4267         is->kind = cp->kind;
4268         is->fd = cp->fdFrom;
4269     }
4270     if (lineByLine) {
4271         is->unused = is->buf;
4272     }
4273
4274     is->xid = XtAppAddInput(appContext, is->fd,
4275                             (XtPointer) (XtInputReadMask),
4276                             (XtInputCallbackProc) DoInputCallback,
4277                             (XtPointer) is);
4278     is->closure = closure;
4279     return (InputSourceRef) is;
4280 }
4281
4282 void
4283 RemoveInputSource (InputSourceRef isr)
4284 {
4285     InputSource *is = (InputSource *) isr;
4286
4287     if (is->xid == 0) return;
4288     XtRemoveInput(is->xid);
4289     is->xid = 0;
4290 }
4291
4292 /****   Animation code by Hugh Fisher, DCS, ANU. ****/
4293
4294 /*      Masks for XPM pieces. Black and white pieces can have
4295         different shapes, but in the interest of retaining my
4296         sanity pieces must have the same outline on both light
4297         and dark squares, and all pieces must use the same
4298         background square colors/images.                */
4299
4300 static int xpmDone = 0;
4301 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
4302 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
4303
4304 static void
4305 CreateAnimMasks (int pieceDepth)
4306 {
4307   ChessSquare   piece;
4308   Pixmap        buf;
4309   GC            bufGC, maskGC;
4310   int           kind, n;
4311   unsigned long plane;
4312   XGCValues     values;
4313
4314   /* Need a bitmap just to get a GC with right depth */
4315   buf = XCreatePixmap(xDisplay, xBoardWindow,
4316                         8, 8, 1);
4317   values.foreground = 1;
4318   values.background = 0;
4319   /* Don't use XtGetGC, not read only */
4320   maskGC = XCreateGC(xDisplay, buf,
4321                     GCForeground | GCBackground, &values);
4322   XFreePixmap(xDisplay, buf);
4323
4324   buf = XCreatePixmap(xDisplay, xBoardWindow,
4325                       squareSize, squareSize, pieceDepth);
4326   values.foreground = XBlackPixel(xDisplay, xScreen);
4327   values.background = XWhitePixel(xDisplay, xScreen);
4328   bufGC = XCreateGC(xDisplay, buf,
4329                     GCForeground | GCBackground, &values);
4330
4331   for (piece = WhitePawn; piece <= BlackKing; piece++) {
4332     /* Begin with empty mask */
4333     if(!xpmDone) // [HGM] pieces: keep using existing
4334     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
4335                                  squareSize, squareSize, 1);
4336     XSetFunction(xDisplay, maskGC, GXclear);
4337     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
4338                    0, 0, squareSize, squareSize);
4339
4340     /* Take a copy of the piece */
4341     if (White(piece))
4342       kind = 0;
4343     else
4344       kind = 2;
4345     XSetFunction(xDisplay, bufGC, GXcopy);
4346     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
4347               buf, bufGC,
4348               0, 0, squareSize, squareSize, 0, 0);
4349
4350     /* XOR the background (light) over the piece */
4351     XSetFunction(xDisplay, bufGC, GXxor);
4352     if (useImageSqs)
4353       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
4354                 0, 0, squareSize, squareSize, 0, 0);
4355     else {
4356       XSetForeground(xDisplay, bufGC, lightSquareColor);
4357       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
4358     }
4359
4360     /* We now have an inverted piece image with the background
4361        erased. Construct mask by just selecting all the non-zero
4362        pixels - no need to reconstruct the original image.      */
4363     XSetFunction(xDisplay, maskGC, GXor);
4364     plane = 1;
4365     /* Might be quicker to download an XImage and create bitmap
4366        data from it rather than this N copies per piece, but it
4367        only takes a fraction of a second and there is a much
4368        longer delay for loading the pieces.             */
4369     for (n = 0; n < pieceDepth; n ++) {
4370       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
4371                  0, 0, squareSize, squareSize,
4372                  0, 0, plane);
4373       plane = plane << 1;
4374     }
4375   }
4376   /* Clean up */
4377   XFreePixmap(xDisplay, buf);
4378   XFreeGC(xDisplay, bufGC);
4379   XFreeGC(xDisplay, maskGC);
4380 }
4381
4382 static void
4383 InitAnimState (AnimNr anr, XWindowAttributes *info)
4384 {
4385   XtGCMask  mask;
4386   XGCValues values;
4387
4388   /* Each buffer is square size, same depth as window */
4389   animBufs[anr+4] = xBoardWindow;
4390   animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
4391                         squareSize, squareSize, info->depth);
4392   animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
4393                         squareSize, squareSize, info->depth);
4394
4395   /* Create a plain GC for blitting */
4396   mask = GCForeground | GCBackground | GCFunction |
4397          GCPlaneMask | GCGraphicsExposures;
4398   values.foreground = XBlackPixel(xDisplay, xScreen);
4399   values.background = XWhitePixel(xDisplay, xScreen);
4400   values.function   = GXcopy;
4401   values.plane_mask = AllPlanes;
4402   values.graphics_exposures = False;
4403   animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
4404
4405   /* Piece will be copied from an existing context at
4406      the start of each new animation/drag. */
4407   animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
4408
4409   /* Outline will be a read-only copy of an existing */
4410   animGCs[anr+4] = None;
4411 }
4412
4413 void
4414 CreateAnimVars ()
4415 {
4416   XWindowAttributes info;
4417
4418   if (xpmDone && gameInfo.variant == oldVariant) return;
4419   if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
4420   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
4421
4422   InitAnimState(Game, &info);
4423   InitAnimState(Player, &info);
4424
4425   /* For XPM pieces, we need bitmaps to use as masks. */
4426   if (useImages)
4427     CreateAnimMasks(info.depth), xpmDone = 1;
4428 }
4429
4430 #ifndef HAVE_USLEEP
4431
4432 static Boolean frameWaiting;
4433
4434 static RETSIGTYPE
4435 FrameAlarm (int sig)
4436 {
4437   frameWaiting = False;
4438   /* In case System-V style signals.  Needed?? */
4439   signal(SIGALRM, FrameAlarm);
4440 }
4441
4442 void
4443 FrameDelay (int time)
4444 {
4445   struct itimerval delay;
4446
4447   XSync(xDisplay, False);
4448
4449   if (time > 0) {
4450     frameWaiting = True;
4451     signal(SIGALRM, FrameAlarm);
4452     delay.it_interval.tv_sec =
4453       delay.it_value.tv_sec = time / 1000;
4454     delay.it_interval.tv_usec =
4455       delay.it_value.tv_usec = (time % 1000) * 1000;
4456     setitimer(ITIMER_REAL, &delay, NULL);
4457     while (frameWaiting) pause();
4458     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
4459     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
4460     setitimer(ITIMER_REAL, &delay, NULL);
4461   }
4462 }
4463
4464 #else
4465
4466 void
4467 FrameDelay (int time)
4468 {
4469   XSync(xDisplay, False);
4470   if (time > 0)
4471     usleep(time * 1000);
4472 }
4473
4474 #endif
4475
4476 static void
4477 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
4478 {
4479   GC source;
4480
4481   /* Bitmap for piece being moved. */
4482   if (appData.monoMode) {
4483       *mask = *pieceToSolid(piece);
4484   } else if (useImages) {
4485 #if HAVE_LIBXPM
4486       *mask = xpmMask[piece];
4487 #else
4488       *mask = ximMaskPm[piece];
4489 #endif
4490   } else {
4491       *mask = *pieceToSolid(piece);
4492   }
4493
4494   /* GC for piece being moved. Square color doesn't matter, but
4495      since it gets modified we make a copy of the original. */
4496   if (White(piece)) {
4497     if (appData.monoMode)
4498       source = bwPieceGC;
4499     else
4500       source = wlPieceGC;
4501   } else {
4502     if (appData.monoMode)
4503       source = wbPieceGC;
4504     else
4505       source = blPieceGC;
4506   }
4507   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
4508
4509   /* Outline only used in mono mode and is not modified */
4510   if (White(piece))
4511     *outline = bwPieceGC;
4512   else
4513     *outline = wbPieceGC;
4514 }
4515
4516 static void
4517 OverlayPiece (ChessSquare piece, GC clip, GC outline,  Drawable dest)
4518 {
4519   int   kind;
4520
4521   if (!useImages) {
4522     /* Draw solid rectangle which will be clipped to shape of piece */
4523     XFillRectangle(xDisplay, dest, clip,
4524                    0, 0, squareSize, squareSize);
4525     if (appData.monoMode)
4526       /* Also draw outline in contrasting color for black
4527          on black / white on white cases                */
4528       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
4529                  0, 0, squareSize, squareSize, 0, 0, 1);
4530   } else {
4531     /* Copy the piece */
4532     if (White(piece))
4533       kind = 0;
4534     else
4535       kind = 2;
4536     if(appData.upsideDown && flipView) kind ^= 2;
4537     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4538               dest, clip,
4539               0, 0, squareSize, squareSize,
4540               0, 0);
4541   }
4542 }
4543
4544 void
4545 InsertPiece (AnimNr anr, ChessSquare piece)
4546 {
4547   OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
4548 }
4549
4550 void
4551 DrawBlank (AnimNr anr, int x, int y, int startColor)
4552 {
4553     BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
4554 }
4555
4556 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
4557                  int srcX, int srcY, int width, int height, int destX, int destY)
4558 {
4559     XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
4560                 srcX, srcY, width, height, destX, destY);
4561 }
4562
4563 void
4564 SetDragPiece (AnimNr anr, ChessSquare piece)
4565 {
4566   Pixmap mask;
4567   /* The piece will be drawn using its own bitmap as a matte    */
4568   SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
4569   XSetClipMask(xDisplay, animGCs[anr+2], mask);
4570 }
4571
4572 /* [AS] Arrow highlighting support */
4573
4574 void
4575 DrawPolygon (Pnt arrow[], int nr)
4576 {
4577     XPoint pts[10];
4578     int i;
4579     for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
4580     XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
4581     if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
4582 }
4583
4584 void
4585 UpdateLogos (int displ)
4586 {
4587     return; // no logos in XBoard yet
4588 }
4589