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