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