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