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