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