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