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