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