d3b827f5801e28a26398578dc56e106953d7d3f6
[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 "engineoutput.h"
210 #include "usystem.h"
211 #include "gettext.h"
212
213
214 #ifdef __EMX__
215 #ifndef HAVE_USLEEP
216 #define HAVE_USLEEP
217 #endif
218 #define usleep(t)   _sleep2(((t)+500)/1000)
219 #endif
220
221 #ifdef ENABLE_NLS
222 # define  _(s) gettext (s)
223 # define N_(s) gettext_noop (s)
224 #else
225 # define  _(s) (s)
226 # define N_(s)  s
227 #endif
228
229 int main P((int argc, char **argv));
230 RETSIGTYPE CmailSigHandler P((int sig));
231 RETSIGTYPE IntSigHandler P((int sig));
232 RETSIGTYPE TermSizeSigHandler P((int sig));
233 static void CreateGCs P((int redo));
234 static void CreateAnyPieces P((void));
235 void CreateXIMPieces P((void));
236 void CreateXPMPieces P((void));
237 void CreateXPMBoard P((char *s, int n));
238 void CreatePieces P((void));
239 Widget CreateMenuBar P((Menu *mb, int boardWidth));
240 #if ENABLE_NLS
241 char *InsertPxlSize P((char *pattern, int targetPxlSize));
242 XFontSet CreateFontSet P((char *base_fnt_lst));
243 #else
244 char *FindFont P((char *pattern, int targetPxlSize));
245 #endif
246 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
247                    u_int wreq, u_int hreq));
248 void CreateGrid P((void));
249 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
250 void DelayedDrag P((void));
251 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
252 void HandlePV P((Widget w, XEvent * event,
253                      String * params, Cardinal * nParams));
254 void DrawPositionProc P((Widget w, XEvent *event,
255                      String *prms, Cardinal *nprms));
256 void CommentClick P((Widget w, XEvent * event,
257                    String * params, Cardinal * nParams));
258 void ICSInputBoxPopUp P((void));
259 void FileNamePopUp P((char *label, char *def, char *filter,
260                       FileProc proc, char *openMode));
261 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
262 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
263 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
264 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
265 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
266 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
267 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
268 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
269 Boolean TempBackwardActive = False;
270 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
271 void DisplayMove P((int moveNumber));
272 void ICSInitScript P((void));
273 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
274 void update_ics_width P(());
275 int get_term_width P(());
276 int CopyMemoProc P(());
277 void SetupDropMenu P((void));
278
279 /*
280 * XBoard depends on Xt R4 or higher
281 */
282 int xtVersion = XtSpecificationRelease;
283
284 int xScreen;
285 Display *xDisplay;
286 Window xBoardWindow;
287 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
288   highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
289 Pixel lowTimeWarningColor;
290 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
291   bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
292   prelineGC, countGC;
293 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
294 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
295 Option *optList; // contains all widgets of main window
296 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
297 #if ENABLE_NLS
298 XFontSet fontSet, clockFontSet;
299 #else
300 Font clockFontID;
301 XFontStruct *clockFontStruct;
302 #endif
303 Font coordFontID, countFontID;
304 XFontStruct *coordFontStruct, *countFontStruct;
305 XtAppContext appContext;
306 char *layoutName;
307
308 FileProc fileProc;
309 char *fileOpenMode;
310 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
311
312 Position commentX = -1, commentY = -1;
313 Dimension commentW, commentH;
314 typedef unsigned int BoardSize;
315 BoardSize boardSize;
316 Boolean chessProgram;
317
318 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
319 int smallLayout = 0, tinyLayout = 0,
320   marginW, marginH, // [HGM] for run-time resizing
321   fromX = -1, fromY = -1, toX, toY, commentUp = False,
322   errorExitStatus = -1, defaultLineGap;
323 Dimension textHeight;
324 Pixel timerForegroundPixel, timerBackgroundPixel;
325 Pixel buttonForegroundPixel, buttonBackgroundPixel;
326 char *chessDir, *programName, *programVersion;
327 Boolean alwaysOnTop = False;
328 char *icsTextMenuString;
329 char *icsNames;
330 char *firstChessProgramNames;
331 char *secondChessProgramNames;
332
333 WindowPlacement wpMain;
334 WindowPlacement wpConsole;
335 WindowPlacement wpComment;
336 WindowPlacement wpMoveHistory;
337 WindowPlacement wpEvalGraph;
338 WindowPlacement wpEngineOutput;
339 WindowPlacement wpGameList;
340 WindowPlacement wpTags;
341
342
343 #define SOLID 0
344 #define OUTLINE 1
345 Pixmap pieceBitmap[2][(int)BlackPawn];
346 Pixmap pieceBitmap2[2][(int)BlackPawn+4];       /* [HGM] pieces */
347 Pixmap xpmPieceBitmap[4][(int)BlackPawn];       /* LL, LD, DL, DD actually used*/
348 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4];    /* LL, LD, DL, DD set to select from */
349 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
350 Pixmap xpmBoardBitmap[2];
351 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
352 XImage *ximPieceBitmap[4][(int)BlackPawn+4];    /* LL, LD, DL, DD */
353 Pixmap ximMaskPm[(int)BlackPawn];               /* clipmasks, used for XIM pieces */
354 Pixmap ximMaskPm2[(int)BlackPawn+4];            /* clipmasks, used for XIM pieces */
355 XImage *ximLightSquare, *ximDarkSquare;
356 XImage *xim_Cross;
357
358 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
359 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
360
361 #define White(piece) ((int)(piece) < (int)BlackPawn)
362
363 /* Bitmaps for use as masks when drawing XPM pieces.
364    Need one for each black and white piece.             */
365 static Pixmap xpmMask[BlackKing + 1];
366
367 /* This magic number is the number of intermediate frames used
368    in each half of the animation. For short moves it's reduced
369    by 1. The total number of frames will be factor * 2 + 1.  */
370 #define kFactor    4
371
372 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
373
374 typedef struct {
375     char piece;
376     char* widget;
377 } DropMenuEnables;
378
379 DropMenuEnables dmEnables[] = {
380     { 'P', "Pawn" },
381     { 'N', "Knight" },
382     { 'B', "Bishop" },
383     { 'R', "Rook" },
384     { 'Q', "Queen" }
385 };
386
387 Arg shellArgs[] = {
388     { XtNwidth, 0 },
389     { XtNheight, 0 },
390     { XtNminWidth, 0 },
391     { XtNminHeight, 0 },
392     { XtNmaxWidth, 0 },
393     { XtNmaxHeight, 0 }
394 };
395
396 XtResource clientResources[] = {
397     { "flashCount", "flashCount", XtRInt, sizeof(int),
398         XtOffset(AppDataPtr, flashCount), XtRImmediate,
399         (XtPointer) FLASH_COUNT  },
400 };
401
402 XrmOptionDescRec shellOptions[] = {
403     { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
404     { "-flash", "flashCount", XrmoptionNoArg, "3" },
405     { "-xflash", "flashCount", XrmoptionNoArg, "0" },
406 };
407
408 XtActionsRec boardActions[] = {
409     { "DrawPosition", DrawPositionProc },
410     { "HandlePV", HandlePV },
411     { "SelectPV", SelectPV },
412     { "StopPV", StopPV },
413     { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
414     { "QuitProc", QuitWrapper },
415     { "ManProc", ManInner },
416     { "TempBackwardProc", TempBackwardProc },
417     { "TempForwardProc", TempForwardProc },
418     { "CommentClick", (XtActionProc) CommentClick },
419     { "GenericPopDown", (XtActionProc) GenericPopDown },
420     { "ErrorPopDown", (XtActionProc) ErrorPopDown },
421     { "CopyMemoProc", (XtActionProc) CopyMemoProc },
422     { "SelectMove", (XtActionProc) SelectMove },
423     { "LoadSelectedProc", LoadSelectedProc },
424     { "SetFilterProc", SetFilterProc },
425     { "TypeInProc", TypeInProc },
426     { "EnterKeyProc", EnterKeyProc },
427     { "UpKeyProc", UpKeyProc },
428     { "DownKeyProc", DownKeyProc },
429     { "WheelProc", WheelProc },
430     { "TabProc", TabProc },
431 };
432
433 char globalTranslations[] =
434   ":<Key>F9: MenuItem(Actions.Resign) \n \
435    :Ctrl<Key>n: MenuItem(File.NewGame) \n \
436    :Meta<Key>V: MenuItem(File.NewVariant) \n \
437    :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
438    :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
439    :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
440    :Ctrl<Key>Down: LoadSelectedProc(3) \n \
441    :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
442    :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
443    :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
444    :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
445    :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
446    :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
447    :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
448    :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
449    :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
450    :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
451    :Ctrl<Key>q: MenuItem(File.Quit) \n \
452    :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
453    :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
454    :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
455    :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
456    :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
457    :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
458    :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
459    :Meta<Key>O: MenuItem(View.EngineOutput) \n \
460    :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
461    :Meta<Key>G: MenuItem(View.GameList) \n \
462    :Meta<Key>H: MenuItem(View.MoveHistory) \n \
463    :<Key>Pause: MenuItem(Mode.Pause) \n \
464    :<Key>F3: MenuItem(Action.Accept) \n \
465    :<Key>F4: MenuItem(Action.Decline) \n \
466    :<Key>F12: MenuItem(Action.Rematch) \n \
467    :<Key>F5: MenuItem(Action.CallFlag) \n \
468    :<Key>F6: MenuItem(Action.Draw) \n \
469    :<Key>F7: MenuItem(Action.Adjourn) \n \
470    :<Key>F8: MenuItem(Action.Abort) \n \
471    :<Key>F10: MenuItem(Action.StopObserving) \n \
472    :<Key>F11: MenuItem(Action.StopExamining) \n \
473    :Ctrl<Key>d: MenuItem(DebugProc) \n \
474    :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
475    :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
476    :Meta<Key>Right: MenuItem(Edit.Forward) \n \
477    :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
478    :Meta<Key>Left: MenuItem(Edit.Backward) \n \
479    :<Key>Left: MenuItem(Edit.Backward) \n \
480    :<Key>Right: MenuItem(Edit.Forward) \n \
481    :<Key>Home: MenuItem(Edit.Revert) \n \
482    :<Key>End: MenuItem(Edit.TruncateGame) \n \
483    :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
484    :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
485    :Meta<Key>J: MenuItem(Options.Adjudications) \n \
486    :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
487    :Meta<Key>T: MenuItem(Options.TimeControl) \n \
488    :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
489 #ifndef OPTIONSDIALOG
490     "\
491    :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
492    :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
493    :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
494    :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
495    :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
496 #endif
497    "\
498    :<Key>F1: MenuItem(Help.ManXBoard) \n \
499    :<Key>F2: MenuItem(View.FlipView) \n \
500    :<KeyDown>Return: TempBackwardProc() \n \
501    :<KeyUp>Return: TempForwardProc() \n";
502
503 char ICSInputTranslations[] =
504     "<Key>Up: UpKeyProc() \n "
505     "<Key>Down: DownKeyProc() \n "
506     "<Key>Return: EnterKeyProc() \n";
507
508 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
509 //             as the widget is destroyed before the up-click can call extend-end
510 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
511
512 String xboardResources[] = {
513     "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
514     NULL
515   };
516
517
518 /* Max possible square size */
519 #define MAXSQSIZE 256
520
521 static int xpm_avail[MAXSQSIZE];
522
523 #ifdef HAVE_DIR_STRUCT
524
525 /* Extract piece size from filename */
526 static int
527 xpm_getsize (char *name, int len, char *ext)
528 {
529     char *p, *d;
530     char buf[10];
531
532     if (len < 4)
533       return 0;
534
535     if ((p=strchr(name, '.')) == NULL ||
536         StrCaseCmp(p+1, ext) != 0)
537       return 0;
538
539     p = name + 3;
540     d = buf;
541
542     while (*p && isdigit(*p))
543       *(d++) = *(p++);
544
545     *d = 0;
546     return atoi(buf);
547 }
548
549 /* Setup xpm_avail */
550 static int
551 xpm_getavail (char *dirname, char *ext)
552 {
553     DIR *dir;
554     struct dirent *ent;
555     int  i;
556
557     for (i=0; i<MAXSQSIZE; ++i)
558       xpm_avail[i] = 0;
559
560     if (appData.debugMode)
561       fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
562
563     dir = opendir(dirname);
564     if (!dir)
565       {
566           fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
567                   programName, dirname);
568           exit(1);
569       }
570
571     while ((ent=readdir(dir)) != NULL) {
572         i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
573         if (i > 0 && i < MAXSQSIZE)
574           xpm_avail[i] = 1;
575     }
576
577     closedir(dir);
578
579     return 0;
580 }
581
582 void
583 xpm_print_avail (FILE *fp, char *ext)
584 {
585     int i;
586
587     fprintf(fp, _("Available `%s' sizes:\n"), ext);
588     for (i=1; i<MAXSQSIZE; ++i) {
589         if (xpm_avail[i])
590           printf("%d\n", i);
591     }
592 }
593
594 /* Return XPM piecesize closest to size */
595 int
596 xpm_closest_to (char *dirname, int size, char *ext)
597 {
598     int i;
599     int sm_diff = MAXSQSIZE;
600     int sm_index = 0;
601     int diff;
602
603     xpm_getavail(dirname, ext);
604
605     if (appData.debugMode)
606       xpm_print_avail(stderr, ext);
607
608     for (i=1; i<MAXSQSIZE; ++i) {
609         if (xpm_avail[i]) {
610             diff = size - i;
611             diff = (diff<0) ? -diff : diff;
612             if (diff < sm_diff) {
613                 sm_diff = diff;
614                 sm_index = i;
615             }
616         }
617     }
618
619     if (!sm_index) {
620         fprintf(stderr, _("Error: No `%s' files!\n"), ext);
621         exit(1);
622     }
623
624     return sm_index;
625 }
626 #else   /* !HAVE_DIR_STRUCT */
627 /* If we are on a system without a DIR struct, we can't
628    read the directory, so we can't collect a list of
629    filenames, etc., so we can't do any size-fitting. */
630 int
631 xpm_closest_to (char *dirname, int size, char *ext)
632 {
633     fprintf(stderr, _("\
634 Warning: No DIR structure found on this system --\n\
635          Unable to autosize for XPM/XIM pieces.\n\
636    Please report this error to %s.\n\
637    Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
638     return size;
639 }
640 #endif /* HAVE_DIR_STRUCT */
641
642
643 /* Arrange to catch delete-window events */
644 Atom wm_delete_window;
645 void
646 CatchDeleteWindow (Widget w, String procname)
647 {
648   char buf[MSG_SIZ];
649   XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
650   snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
651   XtAugmentTranslations(w, XtParseTranslationTable(buf));
652 }
653
654 void
655 BoardToTop ()
656 {
657   Arg args[16];
658   XtSetArg(args[0], XtNiconic, False);
659   XtSetValues(shellWidget, args, 1);
660
661   XtPopup(shellWidget, XtGrabNone); /* Raise if lowered  */
662 }
663
664 //---------------------------------------------------------------------------------------------------------
665 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
666 #define XBOARD True
667 #define JAWS_ARGS
668 #define CW_USEDEFAULT (1<<31)
669 #define ICS_TEXT_MENU_SIZE 90
670 #define DEBUG_FILE "xboard.debug"
671 #define SetCurrentDirectory chdir
672 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
673 #define OPTCHAR "-"
674 #define SEPCHAR " "
675
676 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
677 #include "args.h"
678
679 // front-end part of option handling
680
681 // [HGM] This platform-dependent table provides the location for storing the color info
682 extern char *crWhite, * crBlack;
683
684 void *
685 colorVariable[] = {
686   &appData.whitePieceColor,
687   &appData.blackPieceColor,
688   &appData.lightSquareColor,
689   &appData.darkSquareColor,
690   &appData.highlightSquareColor,
691   &appData.premoveHighlightColor,
692   &appData.lowTimeWarningColor,
693   NULL,
694   NULL,
695   NULL,
696   NULL,
697   NULL,
698   &crWhite,
699   &crBlack,
700   NULL
701 };
702
703 // [HGM] font: keep a font for each square size, even non-stndard ones
704 #define NUM_SIZES 18
705 #define MAX_SIZE 130
706 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
707 char *fontTable[NUM_FONTS][MAX_SIZE];
708
709 void
710 ParseFont (char *name, int number)
711 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
712   int size;
713   if(sscanf(name, "size%d:", &size)) {
714     // [HGM] font: font is meant for specific boardSize (likely from settings file);
715     //       defer processing it until we know if it matches our board size
716     if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
717         fontTable[number][size] = strdup(strchr(name, ':')+1);
718         fontValid[number][size] = True;
719     }
720     return;
721   }
722   switch(number) {
723     case 0: // CLOCK_FONT
724         appData.clockFont = strdup(name);
725       break;
726     case 1: // MESSAGE_FONT
727         appData.font = strdup(name);
728       break;
729     case 2: // COORD_FONT
730         appData.coordFont = strdup(name);
731       break;
732     default:
733       return;
734   }
735   fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
736 }
737
738 void
739 SetFontDefaults ()
740 { // only 2 fonts currently
741   appData.clockFont = CLOCK_FONT_NAME;
742   appData.coordFont = COORD_FONT_NAME;
743   appData.font  =   DEFAULT_FONT_NAME;
744 }
745
746 void
747 CreateFonts ()
748 { // no-op, until we identify the code for this already in XBoard and move it here
749 }
750
751 void
752 ParseColor (int n, char *name)
753 { // in XBoard, just copy the color-name string
754   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
755 }
756
757 void
758 ParseTextAttribs (ColorClass cc, char *s)
759 {
760     (&appData.colorShout)[cc] = strdup(s);
761 }
762
763 void
764 ParseBoardSize (void *addr, char *name)
765 {
766     appData.boardSize = strdup(name);
767 }
768
769 void
770 LoadAllSounds ()
771 { // In XBoard the sound-playing program takes care of obtaining the actual sound
772 }
773
774 void
775 SetCommPortDefaults ()
776 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
777 }
778
779 // [HGM] args: these three cases taken out to stay in front-end
780 void
781 SaveFontArg (FILE *f, ArgDescriptor *ad)
782 {
783   char *name;
784   int i, n = (int)(intptr_t)ad->argLoc;
785   switch(n) {
786     case 0: // CLOCK_FONT
787         name = appData.clockFont;
788       break;
789     case 1: // MESSAGE_FONT
790         name = appData.font;
791       break;
792     case 2: // COORD_FONT
793         name = appData.coordFont;
794       break;
795     default:
796       return;
797   }
798   for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
799     if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
800         fontTable[n][squareSize] = strdup(name);
801         fontValid[n][squareSize] = True;
802         break;
803   }
804   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
805     fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
806 }
807
808 void
809 ExportSounds ()
810 { // nothing to do, as the sounds are at all times represented by their text-string names already
811 }
812
813 void
814 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
815 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
816         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
817 }
818
819 void
820 SaveColor (FILE *f, ArgDescriptor *ad)
821 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
822         if(colorVariable[(int)(intptr_t)ad->argLoc])
823         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
824 }
825
826 void
827 SaveBoardSize (FILE *f, char *name, void *addr)
828 { // wrapper to shield back-end from BoardSize & sizeInfo
829   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
830 }
831
832 void
833 ParseCommPortSettings (char *s)
834 { // no such option in XBoard (yet)
835 }
836
837 int frameX, frameY;
838
839 void
840 GetActualPlacement (Widget wg, WindowPlacement *wp)
841 {
842   Arg args[16];
843   Dimension w, h;
844   Position x, y;
845   XWindowAttributes winAt;
846   Window win, dummy;
847   int i, rx, ry;
848
849   if(!wg) return;
850
851     win = XtWindow(wg);
852     XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
853     XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
854     wp->x = rx - winAt.x;
855     wp->y = ry - winAt.y;
856     wp->height = winAt.height;
857     wp->width = winAt.width;
858     frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
859 }
860
861 void
862 GetWindowCoords ()
863 { // wrapper to shield use of window handles from back-end (make addressible by number?)
864   // In XBoard this will have to wait until awareness of window parameters is implemented
865   GetActualPlacement(shellWidget, &wpMain);
866   if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
867   if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
868   if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
869   if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
870   if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
871   if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
872 }
873
874 void
875 PrintCommPortSettings (FILE *f, char *name)
876 { // This option does not exist in XBoard
877 }
878
879 void
880 EnsureOnScreen (int *x, int *y, int minX, int minY)
881 {
882   return;
883 }
884
885 int
886 MainWindowUp ()
887 { // [HGM] args: allows testing if main window is realized from back-end
888   return xBoardWindow != 0;
889 }
890
891 void SwitchWindow()
892 {
893     extern Option dualOptions[];
894     static Window dual;
895     Window tmp = xBoardWindow;
896     if(!dual) dual = XtWindow(dualOptions[3].handle); // must be first call
897     xBoardWindow = dual; // swap them
898     dual = tmp;
899 }
900
901 void
902 PopUpStartupDialog ()
903 {  // start menu not implemented in XBoard
904 }
905
906 char *
907 ConvertToLine (int argc, char **argv)
908 {
909   static char line[128*1024], buf[1024];
910   int i;
911
912   line[0] = NULLCHAR;
913   for(i=1; i<argc; i++)
914     {
915       if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
916           && argv[i][0] != '{' )
917         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
918       else
919         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
920       strncat(line, buf, 128*1024 - strlen(line) - 1 );
921     }
922
923   line[strlen(line)-1] = NULLCHAR;
924   return line;
925 }
926
927 //--------------------------------------------------------------------------------------------
928
929 #define BoardSize int
930 void
931 InitDrawingSizes (BoardSize boardSize, int flags)
932 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
933     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
934     Arg args[16];
935     XtGeometryResult gres;
936     int i;
937     static Dimension oldWidth, oldHeight;
938     static VariantClass oldVariant;
939     static int oldMono = -1, oldTwoBoards = 0;
940
941     if(!formWidget) return;
942
943     if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
944     oldTwoBoards = twoBoards;
945
946     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
947     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
948     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
949 fprintf(debugFP, "BOARD %d x %d\n",boardWidth, boardHeight);
950   if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
951
952     oldWidth = boardWidth; oldHeight = boardHeight;
953     CreateGrid();
954
955     /*
956      * Inhibit shell resizing.
957      */
958     shellArgs[0].value = w = (XtArgVal) boardWidth + marginW ;
959     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
960     shellArgs[4].value = shellArgs[2].value = w;
961     shellArgs[5].value = shellArgs[3].value = h;
962     XtSetValues(shellWidget, &shellArgs[0], 6);
963
964     XSync(xDisplay, False);
965     DelayedDrag();
966   }
967
968     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
969     // (only for xpm)
970
971   if(gameInfo.variant != oldVariant) { // and only if variant changed
972
973     if(useImages) {
974       for(i=0; i<4; i++) {
975         int p;
976         for(p=0; p<=(int)WhiteKing; p++)
977            xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
978         if(gameInfo.variant == VariantShogi) {
979            xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
980            xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
981            xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
982            xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
983            xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
984         }
985 #ifdef GOTHIC
986         if(gameInfo.variant == VariantGothic) {
987            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
988         }
989 #endif
990         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
991            xpmPieceBitmap[i][(int)WhiteAngel]    = xpmPieceBitmap2[i][(int)WhiteFalcon];
992            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
993         }
994 #if !HAVE_LIBXPM
995         // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
996         for(p=0; p<=(int)WhiteKing; p++)
997            ximMaskPm[p] = ximMaskPm2[p]; // defaults
998         if(gameInfo.variant == VariantShogi) {
999            ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1000            ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1001            ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1002            ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1003            ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1004         }
1005 #ifdef GOTHIC
1006         if(gameInfo.variant == VariantGothic) {
1007            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1008         }
1009 #endif
1010         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1011            ximMaskPm[(int)WhiteAngel]    = ximMaskPm2[(int)WhiteFalcon];
1012            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1013         }
1014 #endif
1015       }
1016     } else {
1017       for(i=0; i<2; i++) {
1018         int p;
1019         for(p=0; p<=(int)WhiteKing; p++)
1020            pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1021         if(gameInfo.variant == VariantShogi) {
1022            pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1023            pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1024            pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1025            pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1026            pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1027         }
1028 #ifdef GOTHIC
1029         if(gameInfo.variant == VariantGothic) {
1030            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1031         }
1032 #endif
1033         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1034            pieceBitmap[i][(int)WhiteAngel]    = pieceBitmap2[i][(int)WhiteFalcon];
1035            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1036         }
1037       }
1038     }
1039     oldMono = -10; // kludge to force recreation of animation masks
1040     oldVariant = gameInfo.variant;
1041   }
1042 #if HAVE_LIBXPM
1043   if(appData.monoMode != oldMono)
1044     CreateAnimVars();
1045 #endif
1046   oldMono = appData.monoMode;
1047 }
1048
1049 static int
1050 MakeOneColor (char *name, Pixel *color)
1051 {
1052     XrmValue vFrom, vTo;
1053     if (!appData.monoMode) {
1054         vFrom.addr = (caddr_t) name;
1055         vFrom.size = strlen(name);
1056         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1057         if (vTo.addr == NULL) {
1058           appData.monoMode = True;
1059           return True;
1060         } else {
1061           *color = *(Pixel *) vTo.addr;
1062         }
1063     }
1064     return False;
1065 }
1066
1067 static int
1068 MakeColors ()
1069 {   // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1070     int forceMono = False;
1071
1072     forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1073     forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1074     forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1075     forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1076     forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1077     forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1078     if (appData.lowTimeWarning)
1079         forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1080     if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1081     if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1082
1083     return forceMono;
1084 }
1085
1086 static void
1087 CreateAnyPieces ()
1088 {   // [HGM] taken out of main
1089 #if HAVE_LIBXPM
1090     if (appData.monoMode && // [HGM] no sense to go on to certain doom
1091        (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1092             appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1093
1094     if (appData.bitmapDirectory[0] != NULLCHAR) {
1095       CreatePieces();
1096     } else {
1097       CreateXPMPieces();
1098       CreateXPMBoard(appData.liteBackTextureFile, 1);
1099       CreateXPMBoard(appData.darkBackTextureFile, 0);
1100     }
1101 #else
1102     CreateXIMPieces();
1103     /* Create regular pieces */
1104     if (!useImages) CreatePieces();
1105 #endif
1106 }
1107
1108 void
1109 InitDrawingParams ()
1110 {
1111     MakeColors(); CreateGCs(True);
1112     CreateAnyPieces();
1113 }
1114
1115 void
1116 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1117 {   // determine what fonts to use, and create them
1118     XrmValue vFrom, vTo;
1119     XrmDatabase xdb;
1120
1121     if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1122         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1123     if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1124         appData.font = fontTable[MESSAGE_FONT][squareSize];
1125     if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1126         appData.coordFont = fontTable[COORD_FONT][squareSize];
1127
1128 #if ENABLE_NLS
1129     appData.font = InsertPxlSize(appData.font, fontPxlSize);
1130     appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1131     appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1132     fontSet = CreateFontSet(appData.font);
1133     clockFontSet = CreateFontSet(appData.clockFont);
1134     {
1135       /* For the coordFont, use the 0th font of the fontset. */
1136       XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1137       XFontStruct **font_struct_list;
1138       XFontSetExtents *fontSize;
1139       char **font_name_list;
1140       XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1141       coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1142       coordFontStruct = XQueryFont(xDisplay, coordFontID);
1143       fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1144       textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1145     }
1146 #else
1147     appData.font = FindFont(appData.font, fontPxlSize);
1148     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1149     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1150     clockFontID = XLoadFont(xDisplay, appData.clockFont);
1151     clockFontStruct = XQueryFont(xDisplay, clockFontID);
1152     coordFontID = XLoadFont(xDisplay, appData.coordFont);
1153     coordFontStruct = XQueryFont(xDisplay, coordFontID);
1154     // textHeight in !NLS mode!
1155 #endif
1156     countFontID = coordFontID;  // [HGM] holdings
1157     countFontStruct = coordFontStruct;
1158
1159     xdb = XtDatabase(xDisplay);
1160 #if ENABLE_NLS
1161     XrmPutLineResource(&xdb, "*international: True");
1162     vTo.size = sizeof(XFontSet);
1163     vTo.addr = (XtPointer) &fontSet;
1164     XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1165 #else
1166     XrmPutStringResource(&xdb, "*font", appData.font);
1167 #endif
1168 }
1169
1170 int
1171 main (int argc, char **argv)
1172 {
1173     int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1174     XSetWindowAttributes window_attributes;
1175     Arg args[16];
1176     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1177     XtGeometryResult gres;
1178     char *p;
1179     int forceMono = False;
1180
1181     srandom(time(0)); // [HGM] book: make random truly random
1182
1183     setbuf(stdout, NULL);
1184     setbuf(stderr, NULL);
1185     debugFP = stderr;
1186
1187     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1188         printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1189         exit(0);
1190     }
1191
1192     programName = strrchr(argv[0], '/');
1193     if (programName == NULL)
1194       programName = argv[0];
1195     else
1196       programName++;
1197
1198 #ifdef ENABLE_NLS
1199     XtSetLanguageProc(NULL, NULL, NULL);
1200     if (appData.debugMode) {
1201       fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1202     }
1203
1204     bindtextdomain(PACKAGE, LOCALEDIR);
1205     textdomain(PACKAGE);
1206 #endif
1207
1208     appData.boardSize = "";
1209     InitAppData(ConvertToLine(argc, argv));
1210     p = getenv("HOME");
1211     if (p == NULL) p = "/tmp";
1212     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1213     gameCopyFilename = (char*) malloc(i);
1214     gamePasteFilename = (char*) malloc(i);
1215     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1216     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1217
1218     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1219         static char buf[MSG_SIZ];
1220         EscapeExpand(buf, appData.firstInitString);
1221         appData.firstInitString = strdup(buf);
1222         EscapeExpand(buf, appData.secondInitString);
1223         appData.secondInitString = strdup(buf);
1224         EscapeExpand(buf, appData.firstComputerString);
1225         appData.firstComputerString = strdup(buf);
1226         EscapeExpand(buf, appData.secondComputerString);
1227         appData.secondComputerString = strdup(buf);
1228     }
1229
1230     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1231         chessDir = ".";
1232     } else {
1233         if (chdir(chessDir) != 0) {
1234             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1235             perror(chessDir);
1236             exit(1);
1237         }
1238     }
1239
1240     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1241         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1242         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
1243            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1244            exit(errno);
1245         }
1246         setbuf(debugFP, NULL);
1247     }
1248
1249     /* [HGM,HR] make sure board size is acceptable */
1250     if(appData.NrFiles > BOARD_FILES ||
1251        appData.NrRanks > BOARD_RANKS   )
1252          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1253
1254 #if !HIGHDRAG
1255     /* This feature does not work; animation needs a rewrite */
1256     appData.highlightDragging = FALSE;
1257 #endif
1258     InitBackEnd1();
1259
1260         gameInfo.variant = StringToVariant(appData.variant);
1261         InitPosition(FALSE);
1262
1263     shellWidget =
1264       XtAppInitialize(&appContext, "XBoard", shellOptions,
1265                       XtNumber(shellOptions),
1266                       &argc, argv, xboardResources, NULL, 0);
1267
1268     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1269                               clientResources, XtNumber(clientResources),
1270                               NULL, 0);
1271
1272     xDisplay = XtDisplay(shellWidget);
1273     xScreen = DefaultScreen(xDisplay);
1274     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1275
1276     /*
1277      * determine size, based on supplied or remembered -size, or screen size
1278      */
1279     if (isdigit(appData.boardSize[0])) {
1280         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1281                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1282                    &fontPxlSize, &smallLayout, &tinyLayout);
1283         if (i == 0) {
1284             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1285                     programName, appData.boardSize);
1286             exit(2);
1287         }
1288         if (i < 7) {
1289             /* Find some defaults; use the nearest known size */
1290             SizeDefaults *szd, *nearest;
1291             int distance = 99999;
1292             nearest = szd = sizeDefaults;
1293             while (szd->name != NULL) {
1294                 if (abs(szd->squareSize - squareSize) < distance) {
1295                     nearest = szd;
1296                     distance = abs(szd->squareSize - squareSize);
1297                     if (distance == 0) break;
1298                 }
1299                 szd++;
1300             }
1301             if (i < 2) lineGap = nearest->lineGap;
1302             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1303             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1304             if (i < 5) fontPxlSize = nearest->fontPxlSize;
1305             if (i < 6) smallLayout = nearest->smallLayout;
1306             if (i < 7) tinyLayout = nearest->tinyLayout;
1307         }
1308     } else {
1309         SizeDefaults *szd = sizeDefaults;
1310         if (*appData.boardSize == NULLCHAR) {
1311             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1312                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1313               szd++;
1314             }
1315             if (szd->name == NULL) szd--;
1316             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1317         } else {
1318             while (szd->name != NULL &&
1319                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1320             if (szd->name == NULL) {
1321                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1322                         programName, appData.boardSize);
1323                 exit(2);
1324             }
1325         }
1326         squareSize = szd->squareSize;
1327         lineGap = szd->lineGap;
1328         clockFontPxlSize = szd->clockFontPxlSize;
1329         coordFontPxlSize = szd->coordFontPxlSize;
1330         fontPxlSize = szd->fontPxlSize;
1331         smallLayout = szd->smallLayout;
1332         tinyLayout = szd->tinyLayout;
1333         // [HGM] font: use defaults from settings file if available and not overruled
1334     }
1335
1336     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1337     if (strlen(appData.pixmapDirectory) > 0) {
1338         p = ExpandPathName(appData.pixmapDirectory);
1339         if (!p) {
1340             fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1341                    appData.pixmapDirectory);
1342             exit(1);
1343         }
1344         if (appData.debugMode) {
1345           fprintf(stderr, _("\
1346 XBoard square size (hint): %d\n\
1347 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1348         }
1349         squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1350         if (appData.debugMode) {
1351             fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1352         }
1353     }
1354     defaultLineGap = lineGap;
1355     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1356
1357     /* [HR] height treated separately (hacked) */
1358     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1359     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1360
1361     /*
1362      * Determine what fonts to use.
1363      */
1364     InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1365
1366     /*
1367      * Detect if there are not enough colors available and adapt.
1368      */
1369     if (DefaultDepth(xDisplay, xScreen) <= 2) {
1370       appData.monoMode = True;
1371     }
1372
1373     forceMono = MakeColors();
1374
1375     if (forceMono) {
1376       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1377               programName);
1378         appData.monoMode = True;
1379     }
1380
1381     if (appData.monoMode && appData.debugMode) {
1382         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1383                 (unsigned long) XWhitePixel(xDisplay, xScreen),
1384                 (unsigned long) XBlackPixel(xDisplay, xScreen));
1385     }
1386
1387     ParseIcsTextColors();
1388
1389     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1390
1391     /*
1392      * widget hierarchy
1393      */
1394     if (tinyLayout) {
1395         layoutName = "tinyLayout";
1396     } else if (smallLayout) {
1397         layoutName = "smallLayout";
1398     } else {
1399         layoutName = "normalLayout";
1400     }
1401
1402     optList = BoardPopUp(squareSize, lineGap, (void*)
1403 #if ENABLE_NLS
1404                                                 &clockFontSet);
1405 #else
1406                                                 &clockFonStruct);
1407 #endif
1408     boardWidget      = optList[W_BOARD].handle;
1409     menuBarWidget    = optList[W_MENU].handle;
1410     dropMenu         = optList[W_DROP].handle;
1411     titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1412     formWidget  = XtParent(boardWidget);
1413     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1414     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1415     XtGetValues(optList[W_WHITE].handle, args, 2);
1416     if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1417       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1418       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1419       XtGetValues(optList[W_PAUSE].handle, args, 2);
1420     }
1421     AppendEnginesToMenu(appData.recentEngineList);
1422
1423     xBoardWindow = XtWindow(boardWidget);
1424
1425     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1426     //       not need to go into InitDrawingSizes().
1427
1428     /*
1429      * Create X checkmark bitmap and initialize option menu checks.
1430      */
1431     ReadBitmap(&xMarkPixmap, "checkmark.bm",
1432                checkmark_bits, checkmark_width, checkmark_height);
1433     InitMenuMarkers();
1434
1435     /*
1436      * Create an icon.
1437      */
1438     ReadBitmap(&wIconPixmap, "icon_white.bm",
1439                icon_white_bits, icon_white_width, icon_white_height);
1440     ReadBitmap(&bIconPixmap, "icon_black.bm",
1441                icon_black_bits, icon_black_width, icon_black_height);
1442     iconPixmap = wIconPixmap;
1443     i = 0;
1444     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
1445     XtSetValues(shellWidget, args, i);
1446
1447     /*
1448      * Create a cursor for the board widget.
1449      */
1450     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1451     XChangeWindowAttributes(xDisplay, xBoardWindow,
1452                             CWCursor, &window_attributes);
1453
1454     /*
1455      * Inhibit shell resizing.
1456      */
1457     shellArgs[0].value = (XtArgVal) &w;
1458     shellArgs[1].value = (XtArgVal) &h;
1459     XtGetValues(shellWidget, shellArgs, 2);
1460     shellArgs[4].value = shellArgs[2].value = w;
1461     shellArgs[5].value = shellArgs[3].value = h;
1462     XtSetValues(shellWidget, &shellArgs[2], 4);
1463     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1464     marginH =  h - boardHeight;
1465
1466     CatchDeleteWindow(shellWidget, "QuitProc");
1467
1468     CreateGCs(False);
1469     CreateGrid();
1470     CreateAnyPieces();
1471
1472     if (appData.animate || appData.animateDragging)
1473       CreateAnimVars();
1474
1475     XtAugmentTranslations(formWidget,
1476                           XtParseTranslationTable(globalTranslations));
1477
1478     XtAddEventHandler(formWidget, KeyPressMask, False,
1479                       (XtEventHandler) MoveTypeInProc, NULL);
1480     XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1481                       (XtEventHandler) EventProc, NULL);
1482
1483     /* [AS] Restore layout */
1484     if( wpMoveHistory.visible ) {
1485       HistoryPopUp();
1486     }
1487
1488     if( wpEvalGraph.visible )
1489       {
1490         EvalGraphPopUp();
1491       };
1492
1493     if( wpEngineOutput.visible ) {
1494       EngineOutputPopUp();
1495     }
1496
1497     InitBackEnd2();
1498
1499     if (errorExitStatus == -1) {
1500         if (appData.icsActive) {
1501             /* We now wait until we see "login:" from the ICS before
1502                sending the logon script (problems with timestamp otherwise) */
1503             /*ICSInitScript();*/
1504             if (appData.icsInputBox) ICSInputBoxPopUp();
1505         }
1506
1507     #ifdef SIGWINCH
1508     signal(SIGWINCH, TermSizeSigHandler);
1509     #endif
1510         signal(SIGINT, IntSigHandler);
1511         signal(SIGTERM, IntSigHandler);
1512         if (*appData.cmailGameName != NULLCHAR) {
1513             signal(SIGUSR1, CmailSigHandler);
1514         }
1515     }
1516
1517     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1518     InitPosition(TRUE);
1519 //    XtSetKeyboardFocus(shellWidget, formWidget);
1520     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1521
1522     XtAppMainLoop(appContext);
1523     if (appData.debugMode) fclose(debugFP); // [DM] debug
1524     return 0;
1525 }
1526
1527 RETSIGTYPE
1528 TermSizeSigHandler (int sig)
1529 {
1530     update_ics_width();
1531 }
1532
1533 RETSIGTYPE
1534 IntSigHandler (int sig)
1535 {
1536     ExitEvent(sig);
1537 }
1538
1539 RETSIGTYPE
1540 CmailSigHandler (int sig)
1541 {
1542     int dummy = 0;
1543     int error;
1544
1545     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
1546
1547     /* Activate call-back function CmailSigHandlerCallBack()             */
1548     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1549
1550     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1551 }
1552
1553 void
1554 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1555 {
1556     BoardToTop();
1557     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
1558 }
1559 /**** end signal code ****/
1560
1561
1562 #define Abs(n) ((n)<0 ? -(n) : (n))
1563
1564 #ifdef ENABLE_NLS
1565 char *
1566 InsertPxlSize (char *pattern, int targetPxlSize)
1567 {
1568     char *base_fnt_lst, strInt[12], *p, *q;
1569     int alternatives, i, len, strIntLen;
1570
1571     /*
1572      * Replace the "*" (if present) in the pixel-size slot of each
1573      * alternative with the targetPxlSize.
1574      */
1575     p = pattern;
1576     alternatives = 1;
1577     while ((p = strchr(p, ',')) != NULL) {
1578       alternatives++;
1579       p++;
1580     }
1581     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1582     strIntLen = strlen(strInt);
1583     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1584
1585     p = pattern;
1586     q = base_fnt_lst;
1587     while (alternatives--) {
1588       char *comma = strchr(p, ',');
1589       for (i=0; i<14; i++) {
1590         char *hyphen = strchr(p, '-');
1591         if (!hyphen) break;
1592         if (comma && hyphen > comma) break;
1593         len = hyphen + 1 - p;
1594         if (i == 7 && *p == '*' && len == 2) {
1595           p += len;
1596           memcpy(q, strInt, strIntLen);
1597           q += strIntLen;
1598           *q++ = '-';
1599         } else {
1600           memcpy(q, p, len);
1601           p += len;
1602           q += len;
1603         }
1604       }
1605       if (!comma) break;
1606       len = comma + 1 - p;
1607       memcpy(q, p, len);
1608       p += len;
1609       q += len;
1610     }
1611     strcpy(q, p);
1612
1613     return base_fnt_lst;
1614 }
1615
1616 XFontSet
1617 CreateFontSet (char *base_fnt_lst)
1618 {
1619     XFontSet fntSet;
1620     char **missing_list;
1621     int missing_count;
1622     char *def_string;
1623
1624     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1625                             &missing_list, &missing_count, &def_string);
1626     if (appData.debugMode) {
1627       int i, count;
1628       XFontStruct **font_struct_list;
1629       char **font_name_list;
1630       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1631       if (fntSet) {
1632         fprintf(debugFP, " got list %s, locale %s\n",
1633                 XBaseFontNameListOfFontSet(fntSet),
1634                 XLocaleOfFontSet(fntSet));
1635         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1636         for (i = 0; i < count; i++) {
1637           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1638         }
1639       }
1640       for (i = 0; i < missing_count; i++) {
1641         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1642       }
1643     }
1644     if (fntSet == NULL) {
1645       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1646       exit(2);
1647     }
1648     return fntSet;
1649 }
1650 #else // not ENABLE_NLS
1651 /*
1652  * Find a font that matches "pattern" that is as close as
1653  * possible to the targetPxlSize.  Prefer fonts that are k
1654  * pixels smaller to fonts that are k pixels larger.  The
1655  * pattern must be in the X Consortium standard format,
1656  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1657  * The return value should be freed with XtFree when no
1658  * longer needed.
1659  */
1660 char *
1661 FindFont (char *pattern, int targetPxlSize)
1662 {
1663     char **fonts, *p, *best, *scalable, *scalableTail;
1664     int i, j, nfonts, minerr, err, pxlSize;
1665
1666     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1667     if (nfonts < 1) {
1668         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1669                 programName, pattern);
1670         exit(2);
1671     }
1672
1673     best = fonts[0];
1674     scalable = NULL;
1675     minerr = 999999;
1676     for (i=0; i<nfonts; i++) {
1677         j = 0;
1678         p = fonts[i];
1679         if (*p != '-') continue;
1680         while (j < 7) {
1681             if (*p == NULLCHAR) break;
1682             if (*p++ == '-') j++;
1683         }
1684         if (j < 7) continue;
1685         pxlSize = atoi(p);
1686         if (pxlSize == 0) {
1687             scalable = fonts[i];
1688             scalableTail = p;
1689         } else {
1690             err = pxlSize - targetPxlSize;
1691             if (Abs(err) < Abs(minerr) ||
1692                 (minerr > 0 && err < 0 && -err == minerr)) {
1693                 best = fonts[i];
1694                 minerr = err;
1695             }
1696         }
1697     }
1698     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1699         /* If the error is too big and there is a scalable font,
1700            use the scalable font. */
1701         int headlen = scalableTail - scalable;
1702         p = (char *) XtMalloc(strlen(scalable) + 10);
1703         while (isdigit(*scalableTail)) scalableTail++;
1704         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1705     } else {
1706         p = (char *) XtMalloc(strlen(best) + 2);
1707         safeStrCpy(p, best, strlen(best)+1 );
1708     }
1709     if (appData.debugMode) {
1710         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
1711                 pattern, targetPxlSize, p);
1712     }
1713     XFreeFontNames(fonts);
1714     return p;
1715 }
1716 #endif
1717
1718 void
1719 DeleteGCs ()
1720 {   // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1721     // must be called before all non-first callse to CreateGCs()
1722     XtReleaseGC(shellWidget, highlineGC);
1723     XtReleaseGC(shellWidget, lightSquareGC);
1724     XtReleaseGC(shellWidget, darkSquareGC);
1725     XtReleaseGC(shellWidget, lineGC);
1726     if (appData.monoMode) {
1727         if (DefaultDepth(xDisplay, xScreen) == 1) {
1728             XtReleaseGC(shellWidget, wbPieceGC);
1729         } else {
1730             XtReleaseGC(shellWidget, bwPieceGC);
1731         }
1732     } else {
1733         XtReleaseGC(shellWidget, prelineGC);
1734         XtReleaseGC(shellWidget, wdPieceGC);
1735         XtReleaseGC(shellWidget, wlPieceGC);
1736         XtReleaseGC(shellWidget, bdPieceGC);
1737         XtReleaseGC(shellWidget, blPieceGC);
1738     }
1739 }
1740
1741 static GC
1742 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1743 {
1744     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1745       | GCBackground | GCFunction | GCPlaneMask;
1746     gc_values->foreground = foreground;
1747     gc_values->background = background;
1748     return XtGetGC(shellWidget, value_mask, gc_values);
1749 }
1750
1751 static void
1752 CreateGCs (int redo)
1753 {
1754     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1755       | GCBackground | GCFunction | GCPlaneMask;
1756     XGCValues gc_values;
1757     GC copyInvertedGC;
1758     Pixel white = XWhitePixel(xDisplay, xScreen);
1759     Pixel black = XBlackPixel(xDisplay, xScreen);
1760
1761     gc_values.plane_mask = AllPlanes;
1762     gc_values.line_width = lineGap;
1763     gc_values.line_style = LineSolid;
1764     gc_values.function = GXcopy;
1765
1766   if(redo) {
1767     DeleteGCs(); // called a second time; clean up old GCs first
1768   } else { // [HGM] grid and font GCs created on first call only
1769     coordGC = CreateOneGC(&gc_values, black, white);
1770     XSetFont(xDisplay, coordGC, coordFontID);
1771
1772     // [HGM] make font for holdings counts (white on black)
1773     countGC = CreateOneGC(&gc_values, white, black);
1774     XSetFont(xDisplay, countGC, countFontID);
1775   }
1776     lineGC = CreateOneGC(&gc_values, black, black);
1777
1778     if (appData.monoMode) {
1779
1780         highlineGC = CreateOneGC(&gc_values, white, white);
1781         lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1782         darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1783
1784         if (DefaultDepth(xDisplay, xScreen) == 1) {
1785             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1786             gc_values.function = GXcopyInverted;
1787             copyInvertedGC = CreateOneGC(&gc_values, black, white);
1788             gc_values.function = GXcopy;
1789             if (XBlackPixel(xDisplay, xScreen) == 1) {
1790                 bwPieceGC = darkSquareGC;
1791                 wbPieceGC = copyInvertedGC;
1792             } else {
1793                 bwPieceGC = copyInvertedGC;
1794                 wbPieceGC = lightSquareGC;
1795             }
1796         }
1797     } else {
1798
1799         highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1800         prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1801         lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1802         darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1803         wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1804         wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1805         bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1806         blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1807     }
1808 }
1809
1810 void
1811 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1812 {
1813     int x, y, w, h, p;
1814     FILE *fp;
1815     Pixmap temp;
1816     XGCValues   values;
1817     GC maskGC;
1818
1819     fp = fopen(filename, "rb");
1820     if (!fp) {
1821         fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1822         exit(1);
1823     }
1824
1825     w = fgetc(fp);
1826     h = fgetc(fp);
1827
1828     for (y=0; y<h; ++y) {
1829         for (x=0; x<h; ++x) {
1830             p = fgetc(fp);
1831
1832             switch (p) {
1833               case 0:
1834                 XPutPixel(xim, x, y, blackPieceColor);
1835                 if (xmask)
1836                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1837                 break;
1838               case 1:
1839                 XPutPixel(xim, x, y, darkSquareColor);
1840                 if (xmask)
1841                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1842                 break;
1843               case 2:
1844                 XPutPixel(xim, x, y, whitePieceColor);
1845                 if (xmask)
1846                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1847                 break;
1848               case 3:
1849                 XPutPixel(xim, x, y, lightSquareColor);
1850                 if (xmask)
1851                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1852                 break;
1853             }
1854         }
1855     }
1856
1857     fclose(fp);
1858
1859     /* create Pixmap of piece */
1860     *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1861                           w, h, xim->depth);
1862     XPutImage(xDisplay, *dest, lightSquareGC, xim,
1863               0, 0, 0, 0, w, h);
1864
1865     /* create Pixmap of clipmask
1866        Note: We assume the white/black pieces have the same
1867              outline, so we make only 6 masks. This is okay
1868              since the XPM clipmask routines do the same. */
1869     if (xmask) {
1870       temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1871                             w, h, xim->depth);
1872       XPutImage(xDisplay, temp, lightSquareGC, xmask,
1873               0, 0, 0, 0, w, h);
1874
1875       /* now create the 1-bit version */
1876       *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1877                           w, h, 1);
1878
1879       values.foreground = 1;
1880       values.background = 0;
1881
1882       /* Don't use XtGetGC, not read only */
1883       maskGC = XCreateGC(xDisplay, *mask,
1884                     GCForeground | GCBackground, &values);
1885       XCopyPlane(xDisplay, temp, *mask, maskGC,
1886                   0, 0, squareSize, squareSize, 0, 0, 1);
1887       XFreePixmap(xDisplay, temp);
1888     }
1889 }
1890
1891
1892 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1893
1894 void
1895 CreateXIMPieces ()
1896 {
1897     int piece, kind;
1898     char buf[MSG_SIZ];
1899     u_int ss;
1900     static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1901     XImage *ximtemp;
1902
1903     ss = squareSize;
1904
1905     /* The XSynchronize calls were copied from CreatePieces.
1906        Not sure if needed, but can't hurt */
1907     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1908                                      buffering bug */
1909
1910     /* temp needed by loadXIM() */
1911     ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1912                  0, 0, ss, ss, AllPlanes, XYPixmap);
1913
1914     if (strlen(appData.pixmapDirectory) == 0) {
1915       useImages = 0;
1916     } else {
1917         useImages = 1;
1918         if (appData.monoMode) {
1919           DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1920                             0, 2);
1921           ExitEvent(2);
1922         }
1923         fprintf(stderr, _("\nLoading XIMs...\n"));
1924         /* Load pieces */
1925         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1926             fprintf(stderr, "%d", piece+1);
1927             for (kind=0; kind<4; kind++) {
1928                 fprintf(stderr, ".");
1929                 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
1930                         ExpandPathName(appData.pixmapDirectory),
1931                         piece <= (int) WhiteKing ? "" : "w",
1932                         pieceBitmapNames[piece],
1933                         ximkind[kind], ss);
1934                 ximPieceBitmap[kind][piece] =
1935                   XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1936                             0, 0, ss, ss, AllPlanes, XYPixmap);
1937                 if (appData.debugMode)
1938                   fprintf(stderr, _("(File:%s:) "), buf);
1939                 loadXIM(ximPieceBitmap[kind][piece],
1940                         ximtemp, buf,
1941                         &(xpmPieceBitmap2[kind][piece]),
1942                         &(ximMaskPm2[piece]));
1943                 if(piece <= (int)WhiteKing)
1944                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
1945             }
1946             fprintf(stderr," ");
1947         }
1948         /* Load light and dark squares */
1949         /* If the LSQ and DSQ pieces don't exist, we will
1950            draw them with solid squares. */
1951         snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
1952         if (access(buf, 0) != 0) {
1953             useImageSqs = 0;
1954         } else {
1955             useImageSqs = 1;
1956             fprintf(stderr, _("light square "));
1957             ximLightSquare=
1958               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1959                         0, 0, ss, ss, AllPlanes, XYPixmap);
1960             if (appData.debugMode)
1961               fprintf(stderr, _("(File:%s:) "), buf);
1962
1963             loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
1964             fprintf(stderr, _("dark square "));
1965             snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
1966                     ExpandPathName(appData.pixmapDirectory), ss);
1967             if (appData.debugMode)
1968               fprintf(stderr, _("(File:%s:) "), buf);
1969             ximDarkSquare=
1970               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1971                         0, 0, ss, ss, AllPlanes, XYPixmap);
1972             loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
1973             xpmJailSquare = xpmLightSquare;
1974         }
1975         fprintf(stderr, _("Done.\n"));
1976     }
1977     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
1978 }
1979
1980 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
1981
1982 #if HAVE_LIBXPM
1983 void
1984 CreateXPMBoard (char *s, int kind)
1985 {
1986     XpmAttributes attr;
1987     attr.valuemask = 0;
1988     if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
1989     if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
1990         useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
1991     }
1992 }
1993
1994 void
1995 FreeXPMPieces ()
1996 {   // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
1997     // thisroutine has to be called t free the old piece pixmaps
1998     int piece, kind;
1999     for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2000         for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2001     if(useImageSqs) {
2002         XFreePixmap(xDisplay, xpmLightSquare);
2003         XFreePixmap(xDisplay, xpmDarkSquare);
2004     }
2005 }
2006
2007 void
2008 CreateXPMPieces ()
2009 {
2010     int piece, kind, r;
2011     char buf[MSG_SIZ];
2012     u_int ss = squareSize;
2013     XpmAttributes attr;
2014     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2015     XpmColorSymbol symbols[4];
2016     static int redo = False;
2017
2018     if(redo) FreeXPMPieces(); else redo = 1;
2019
2020     /* The XSynchronize calls were copied from CreatePieces.
2021        Not sure if needed, but can't hurt */
2022     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2023
2024     /* Setup translations so piece colors match square colors */
2025     symbols[0].name = "light_piece";
2026     symbols[0].value = appData.whitePieceColor;
2027     symbols[1].name = "dark_piece";
2028     symbols[1].value = appData.blackPieceColor;
2029     symbols[2].name = "light_square";
2030     symbols[2].value = appData.lightSquareColor;
2031     symbols[3].name = "dark_square";
2032     symbols[3].value = appData.darkSquareColor;
2033
2034     attr.valuemask = XpmColorSymbols;
2035     attr.colorsymbols = symbols;
2036     attr.numsymbols = 4;
2037
2038     if (appData.monoMode) {
2039       DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2040                         0, 2);
2041       ExitEvent(2);
2042     }
2043     if (strlen(appData.pixmapDirectory) == 0) {
2044         XpmPieces* pieces = builtInXpms;
2045         useImages = 1;
2046         /* Load pieces */
2047         while (pieces->size != squareSize && pieces->size) pieces++;
2048         if (!pieces->size) {
2049           fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2050           exit(1);
2051         }
2052         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2053             for (kind=0; kind<4; kind++) {
2054
2055                 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2056                                                pieces->xpm[piece][kind],
2057                                                &(xpmPieceBitmap2[kind][piece]),
2058                                                NULL, &attr)) != 0) {
2059                   fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2060                           r, buf);
2061                   exit(1);
2062                 }
2063                 if(piece <= (int) WhiteKing)
2064                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2065             }
2066         }
2067         useImageSqs = 0;
2068         xpmJailSquare = xpmLightSquare;
2069     } else {
2070         useImages = 1;
2071
2072         fprintf(stderr, _("\nLoading XPMs...\n"));
2073
2074         /* Load pieces */
2075         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2076             fprintf(stderr, "%d ", piece+1);
2077             for (kind=0; kind<4; kind++) {
2078               snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2079                         ExpandPathName(appData.pixmapDirectory),
2080                         piece > (int) WhiteKing ? "w" : "",
2081                         pieceBitmapNames[piece],
2082                         xpmkind[kind], ss);
2083                 if (appData.debugMode) {
2084                     fprintf(stderr, _("(File:%s:) "), buf);
2085                 }
2086                 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2087                                            &(xpmPieceBitmap2[kind][piece]),
2088                                            NULL, &attr)) != 0) {
2089                     if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2090                       // [HGM] missing: read of unorthodox piece failed; substitute King.
2091                       snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2092                                 ExpandPathName(appData.pixmapDirectory),
2093                                 xpmkind[kind], ss);
2094                         if (appData.debugMode) {
2095                             fprintf(stderr, _("(Replace by File:%s:) "), buf);
2096                         }
2097                         r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2098                                                 &(xpmPieceBitmap2[kind][piece]),
2099                                                 NULL, &attr);
2100                     }
2101                     if (r != 0) {
2102                         fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2103                                 r, buf);
2104                         exit(1);
2105                     }
2106                 }
2107                 if(piece <= (int) WhiteKing)
2108                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2109             }
2110         }
2111         /* Load light and dark squares */
2112         /* If the LSQ and DSQ pieces don't exist, we will
2113            draw them with solid squares. */
2114         fprintf(stderr, _("light square "));
2115         snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2116         if (access(buf, 0) != 0) {
2117             useImageSqs = 0;
2118         } else {
2119             useImageSqs = 1;
2120             if (appData.debugMode)
2121               fprintf(stderr, _("(File:%s:) "), buf);
2122
2123             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2124                                        &xpmLightSquare, NULL, &attr)) != 0) {
2125                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2126                 exit(1);
2127             }
2128             fprintf(stderr, _("dark square "));
2129             snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2130                     ExpandPathName(appData.pixmapDirectory), ss);
2131             if (appData.debugMode) {
2132                 fprintf(stderr, _("(File:%s:) "), buf);
2133             }
2134             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2135                                        &xpmDarkSquare, NULL, &attr)) != 0) {
2136                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2137                 exit(1);
2138             }
2139         }
2140         xpmJailSquare = xpmLightSquare;
2141         fprintf(stderr, _("Done.\n"));
2142     }
2143     oldVariant = -1; // kludge to force re-makig of animation masks
2144     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2145                                       buffering bug */
2146 }
2147 #endif /* HAVE_LIBXPM */
2148
2149 #if HAVE_LIBXPM
2150 /* No built-in bitmaps */
2151 void CreatePieces()
2152 {
2153     int piece, kind;
2154     char buf[MSG_SIZ];
2155     u_int ss = squareSize;
2156
2157     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2158                                      buffering bug */
2159
2160     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2161         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2162           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2163                    pieceBitmapNames[piece],
2164                    ss, kind == SOLID ? 's' : 'o');
2165           ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2166           if(piece <= (int)WhiteKing)
2167             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2168         }
2169     }
2170
2171     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2172                                       buffering bug */
2173 }
2174 #else
2175 /* With built-in bitmaps */
2176 void
2177 CreatePieces ()
2178 {
2179     BuiltInBits* bib = builtInBits;
2180     int piece, kind;
2181     char buf[MSG_SIZ];
2182     u_int ss = squareSize;
2183
2184     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2185                                      buffering bug */
2186
2187     while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2188
2189     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2190         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2191           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2192                    pieceBitmapNames[piece],
2193                    ss, kind == SOLID ? 's' : 'o');
2194           ReadBitmap(&pieceBitmap2[kind][piece], buf,
2195                      bib->bits[kind][piece], ss, ss);
2196           if(piece <= (int)WhiteKing)
2197             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2198         }
2199     }
2200
2201     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2202                                       buffering bug */
2203 }
2204 #endif
2205
2206 void
2207 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2208 {
2209     int x_hot, y_hot;
2210     u_int w, h;
2211     int errcode;
2212     char msg[MSG_SIZ], fullname[MSG_SIZ];
2213
2214     if (*appData.bitmapDirectory != NULLCHAR) {
2215       safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2216       strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2217       strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2218       errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2219                                 &w, &h, pm, &x_hot, &y_hot);
2220       fprintf(stderr, "load %s\n", name);
2221         if (errcode != BitmapSuccess) {
2222             switch (errcode) {
2223               case BitmapOpenFailed:
2224                 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2225                 break;
2226               case BitmapFileInvalid:
2227                 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2228                 break;
2229               case BitmapNoMemory:
2230                 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2231                         fullname);
2232                 break;
2233               default:
2234                 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2235                         errcode, fullname);
2236                 break;
2237             }
2238             fprintf(stderr, _("%s: %s...using built-in\n"),
2239                     programName, msg);
2240         } else if (w != wreq || h != hreq) {
2241             fprintf(stderr,
2242                     _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2243                     programName, fullname, w, h, wreq, hreq);
2244         } else {
2245             return;
2246         }
2247     }
2248     if (bits != NULL) {
2249         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2250                                     wreq, hreq);
2251     }
2252 }
2253
2254 void
2255 CreateGrid ()
2256 {
2257     int i, j;
2258
2259     if (lineGap == 0) return;
2260
2261     /* [HR] Split this into 2 loops for non-square boards. */
2262
2263     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2264         gridSegments[i].x1 = 0;
2265         gridSegments[i].x2 =
2266           lineGap + BOARD_WIDTH * (squareSize + lineGap);
2267         gridSegments[i].y1 = gridSegments[i].y2
2268           = lineGap / 2 + (i * (squareSize + lineGap));
2269     }
2270
2271     for (j = 0; j < BOARD_WIDTH + 1; j++) {
2272         gridSegments[j + i].y1 = 0;
2273         gridSegments[j + i].y2 =
2274           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2275         gridSegments[j + i].x1 = gridSegments[j + i].x2
2276           = lineGap / 2 + (j * (squareSize + lineGap));
2277     }
2278 }
2279
2280 void
2281 MarkMenuItem (char *menuRef, int state)
2282 {
2283     MenuItem *item = MenuNameToItem(menuRef);
2284
2285     if(item) {
2286         Arg args[2];
2287         XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2288         XtSetValues(item->handle, args, 1);
2289     }
2290 }
2291
2292 void
2293 EnableMenuItem (char *menuRef, int state)
2294 {
2295     MenuItem *item = MenuNameToItem(menuRef);
2296
2297     if(item) XtSetSensitive(item->handle, state);
2298 }
2299
2300 void
2301 EnableButtonBar (int state)
2302 {
2303     XtSetSensitive(optList[W_BUTTON].handle, state);
2304 }
2305
2306
2307 void
2308 SetMenuEnables (Enables *enab)
2309 {
2310   while (enab->name != NULL) {
2311     EnableMenuItem(enab->name, enab->value);
2312     enab++;
2313   }
2314 }
2315
2316 void
2317 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2318 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2319     int i;
2320     char *p;
2321     MenuItem *item;
2322     if(*nprms == 0) return;
2323     item = MenuNameToItem(prms[0]);
2324     if(item) ((MenuProc *) item->proc) ();
2325 }
2326
2327 static void
2328 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2329 {
2330     MenuProc *proc = (MenuProc *) addr;
2331
2332     (proc)();
2333 }
2334
2335 static void
2336 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2337 {
2338     RecentEngineEvent((int) (intptr_t) addr);
2339 }
2340
2341 void
2342 AppendMenuItem (char *msg, int n)
2343 {
2344     CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2345 }
2346
2347 void
2348 SetupDropMenu ()
2349 {
2350     int i, j, count;
2351     char label[32];
2352     Arg args[16];
2353     Widget entry;
2354     char* p;
2355
2356     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2357         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2358         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2359                    dmEnables[i].piece);
2360         XtSetSensitive(entry, p != NULL || !appData.testLegality
2361                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2362                                        && !appData.icsActive));
2363         count = 0;
2364         while (p && *p++ == dmEnables[i].piece) count++;
2365         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
2366         j = 0;
2367         XtSetArg(args[j], XtNlabel, label); j++;
2368         XtSetValues(entry, args, j);
2369     }
2370 }
2371
2372
2373 static void
2374 do_flash_delay (unsigned long msec)
2375 {
2376     TimeDelay(msec);
2377 }
2378
2379 void
2380 DrawBorder (int x, int y, int type)
2381 {
2382     GC gc = lineGC;
2383
2384     if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
2385
2386     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
2387                    squareSize+lineGap, squareSize+lineGap);
2388 }
2389
2390 static int
2391 CutOutSquare (int x, int y, int *x0, int *y0, int  kind)
2392 {
2393     int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2394     int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2395     *x0 = 0; *y0 = 0;
2396     if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2397     if(textureW[kind] < W*squareSize)
2398         *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2399     else
2400         *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2401     if(textureH[kind] < H*squareSize)
2402         *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2403     else
2404         *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2405     return 1;
2406 }
2407
2408 static void
2409 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2410 {   // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2411     int x0, y0;
2412     if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2413         XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2414                   squareSize, squareSize, x*fac, y*fac);
2415     } else
2416     if (useImages && useImageSqs) {
2417         Pixmap pm;
2418         switch (color) {
2419           case 1: /* light */
2420             pm = xpmLightSquare;
2421             break;
2422           case 0: /* dark */
2423             pm = xpmDarkSquare;
2424             break;
2425           case 2: /* neutral */
2426           default:
2427             pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2428             break;
2429         }
2430         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2431                   squareSize, squareSize, x*fac, y*fac);
2432     } else {
2433         GC gc;
2434         switch (color) {
2435           case 1: /* light */
2436             gc = lightSquareGC;
2437             break;
2438           case 0: /* dark */
2439             gc = darkSquareGC;
2440             break;
2441           case 2: /* neutral */
2442           default:
2443             gc = lineGC;
2444             break;
2445         }
2446         XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2447     }
2448 }
2449
2450 /*
2451    I split out the routines to draw a piece so that I could
2452    make a generic flash routine.
2453 */
2454 static void
2455 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2456 {
2457     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2458     switch (square_color) {
2459       case 1: /* light */
2460       case 2: /* neutral */
2461       default:
2462         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2463                   ? *pieceToOutline(piece)
2464                   : *pieceToSolid(piece),
2465                   dest, bwPieceGC, 0, 0,
2466                   squareSize, squareSize, x, y);
2467         break;
2468       case 0: /* dark */
2469         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2470                   ? *pieceToSolid(piece)
2471                   : *pieceToOutline(piece),
2472                   dest, wbPieceGC, 0, 0,
2473                   squareSize, squareSize, x, y);
2474         break;
2475     }
2476 }
2477
2478 static void
2479 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2480 {
2481     switch (square_color) {
2482       case 1: /* light */
2483       case 2: /* neutral */
2484       default:
2485         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2486                    ? *pieceToOutline(piece)
2487                    : *pieceToSolid(piece),
2488                    dest, bwPieceGC, 0, 0,
2489                    squareSize, squareSize, x, y, 1);
2490         break;
2491       case 0: /* dark */
2492         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2493                    ? *pieceToSolid(piece)
2494                    : *pieceToOutline(piece),
2495                    dest, wbPieceGC, 0, 0,
2496                    squareSize, squareSize, x, y, 1);
2497         break;
2498     }
2499 }
2500
2501 static void
2502 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2503 {
2504     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2505     switch (square_color) {
2506       case 1: /* light */
2507         XCopyPlane(xDisplay, *pieceToSolid(piece),
2508                    dest, (int) piece < (int) BlackPawn
2509                    ? wlPieceGC : blPieceGC, 0, 0,
2510                    squareSize, squareSize, x, y, 1);
2511         break;
2512       case 0: /* dark */
2513         XCopyPlane(xDisplay, *pieceToSolid(piece),
2514                    dest, (int) piece < (int) BlackPawn
2515                    ? wdPieceGC : bdPieceGC, 0, 0,
2516                    squareSize, squareSize, x, y, 1);
2517         break;
2518       case 2: /* neutral */
2519       default:
2520         break; // should never contain pieces
2521     }
2522 }
2523
2524 static void
2525 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2526 {
2527     int kind, p = piece;
2528
2529     switch (square_color) {
2530       case 1: /* light */
2531       case 2: /* neutral */
2532       default:
2533         if ((int)piece < (int) BlackPawn) {
2534             kind = 0;
2535         } else {
2536             kind = 2;
2537             piece -= BlackPawn;
2538         }
2539         break;
2540       case 0: /* dark */
2541         if ((int)piece < (int) BlackPawn) {
2542             kind = 1;
2543         } else {
2544             kind = 3;
2545             piece -= BlackPawn;
2546         }
2547         break;
2548     }
2549     if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2550     if(useTexture & square_color+1) {
2551         BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2552         XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2553         XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2554         XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2555         XSetClipMask(xDisplay, wlPieceGC, None);
2556         XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2557     } else
2558     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2559               dest, wlPieceGC, 0, 0,
2560               squareSize, squareSize, x, y);
2561 }
2562
2563 typedef void (*DrawFunc)();
2564
2565 DrawFunc
2566 ChooseDrawFunc ()
2567 {
2568     if (appData.monoMode) {
2569         if (DefaultDepth(xDisplay, xScreen) == 1) {
2570             return monoDrawPiece_1bit;
2571         } else {
2572             return monoDrawPiece;
2573         }
2574     } else {
2575         if (useImages)
2576           return colorDrawPieceImage;
2577         else
2578           return colorDrawPiece;
2579     }
2580 }
2581
2582 void
2583 DrawDot (int marker, int x, int y, int r)
2584 {
2585         if(appData.monoMode) {
2586             XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
2587                     x, y, r, r, 0, 64*360);
2588             XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
2589                     x, y, r, r, 0, 64*360);
2590         } else
2591         XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
2592                     x, y, r, r, 0, 64*360);
2593 }
2594
2595 void
2596 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2597 {   // basic front-end board-draw function: takes care of everything that can be in square:
2598     // piece, background, coordinate/count, marker dot
2599     int direction, font_ascent, font_descent;
2600     XCharStruct overall;
2601     DrawFunc drawfunc;
2602
2603     if (piece == EmptySquare) {
2604         BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2605     } else {
2606         drawfunc = ChooseDrawFunc();
2607         drawfunc(piece, square_color, x, y, xBoardWindow);
2608     }
2609
2610     if(align) { // square carries inscription (coord or piece count)
2611         int xx = x, yy = y;
2612         GC hGC = align < 3 ? coordGC : countGC;
2613         // first calculate where it goes
2614         XTextExtents(countFontStruct, string, 1, &direction,
2615                          &font_ascent, &font_descent, &overall);
2616         if (align == 1) {
2617             xx += squareSize - overall.width - 2;
2618             yy += squareSize - font_descent - 1;
2619         } else if (align == 2) {
2620             xx += 2, yy += font_ascent + 1;
2621         } else if (align == 3) {
2622             xx += squareSize - overall.width - 2;
2623             yy += font_ascent + 1;
2624         } else if (align == 4) {
2625             xx += 2, yy += font_ascent + 1;
2626         }
2627         // then draw it
2628         if (appData.monoMode) {
2629             XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2630         } else {
2631             XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2632         }
2633     }
2634
2635     if(marker) { // print fat marker dot, if requested
2636         DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2637     }
2638 }
2639
2640 void
2641 FlashDelay (int flash_delay)
2642 {
2643         XSync(xDisplay, False);
2644         if(flash_delay) do_flash_delay(flash_delay);
2645 }
2646
2647 double
2648 Fraction (int x, int start, int stop)
2649 {
2650    double f = ((double) x - start)/(stop - start);
2651    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2652    return f;
2653 }
2654
2655 static WindowPlacement wpNew;
2656
2657 void
2658 CoDrag (Widget sh, WindowPlacement *wp)
2659 {
2660     Arg args[16];
2661     int j=0, touch=0, fudge = 2;
2662     GetActualPlacement(sh, wp);
2663     if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x)         < fudge) touch = 1; else // right touch
2664     if(abs(wp->x + wp->width + 2*frameX - wpMain.x)            < fudge) touch = 2; else // left touch
2665     if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2666     if(abs(wp->y + wp->height + frameX + frameY - wpMain.y)    < fudge) touch = 4;      // top touch
2667     if(!touch ) return; // only windows that touch co-move
2668     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2669         int heightInc = wpNew.height - wpMain.height;
2670         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2671         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2672         wp->y += fracTop * heightInc;
2673         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2674         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2675     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2676         int widthInc = wpNew.width - wpMain.width;
2677         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2678         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2679         wp->y += fracLeft * widthInc;
2680         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2681         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2682     }
2683     wp->x += wpNew.x - wpMain.x;
2684     wp->y += wpNew.y - wpMain.y;
2685     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2686     if(touch == 3) wp->y += wpNew.height - wpMain.height;
2687     XtSetArg(args[j], XtNx, wp->x); j++;
2688     XtSetArg(args[j], XtNy, wp->y); j++;
2689     XtSetValues(sh, args, j);
2690 }
2691
2692 static XtIntervalId delayedDragID = 0;
2693
2694 void
2695 DragProc ()
2696 {
2697         GetActualPlacement(shellWidget, &wpNew);
2698         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2699            wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2700             return; // false alarm
2701         if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2702         if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2703         if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2704         if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2705         wpMain = wpNew;
2706         DrawPosition(True, NULL);
2707         delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2708 }
2709
2710
2711 void
2712 DelayedDrag ()
2713 {
2714     if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2715     delayedDragID =
2716       XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2717 }
2718
2719 void
2720 EventProc (Widget widget, caddr_t unused, XEvent *event)
2721 {
2722     if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2723         DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2724 }
2725
2726 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
2727 void
2728 DrawSeekAxis (int x, int y, int xTo, int yTo)
2729 {
2730       XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
2731 }
2732
2733 void
2734 DrawSeekBackground (int left, int top, int right, int bottom)
2735 {
2736     XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
2737 }
2738
2739 void
2740 DrawSeekText (char *buf, int x, int y)
2741 {
2742     XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
2743 }
2744
2745 void
2746 DrawSeekDot (int x, int y, int colorNr)
2747 {
2748     int square = colorNr & 0x80;
2749     GC color;
2750     colorNr &= 0x7F;
2751     color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
2752     if(square)
2753         XFillRectangle(xDisplay, xBoardWindow, color,
2754                 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2755     else
2756         XFillArc(xDisplay, xBoardWindow, color,
2757                 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
2758 }
2759
2760 void
2761 DrawGrid ()
2762 {
2763           XDrawSegments(xDisplay, xBoardWindow, lineGC,
2764                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
2765 }
2766
2767
2768 /*
2769  * event handler for redrawing the board
2770  */
2771 void
2772 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2773 {
2774     DrawPosition(True, NULL);
2775 }
2776
2777
2778 void
2779 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
2780 {   // [HGM] pv: walk PV
2781     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2782 }
2783
2784 static int savedIndex;  /* gross that this is global */
2785
2786 void
2787 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
2788 {
2789         String val;
2790         XawTextPosition index, dummy;
2791         Arg arg;
2792
2793         XawTextGetSelectionPos(w, &index, &dummy);
2794         XtSetArg(arg, XtNstring, &val);
2795         XtGetValues(w, &arg, 1);
2796         ReplaceComment(savedIndex, val);
2797         if(savedIndex != currentMove) ToNrEvent(savedIndex);
2798         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
2799 }
2800
2801 void
2802 EditCommentPopUp (int index, char *title, char *text)
2803 {
2804     savedIndex = index;
2805     if (text == NULL) text = "";
2806     NewCommentPopup(title, text, index);
2807 }
2808
2809 void
2810 CommentPopUp (char *title, char *text)
2811 {
2812     savedIndex = currentMove; // [HGM] vari
2813     NewCommentPopup(title, text, currentMove);
2814 }
2815
2816 void
2817 CommentPopDown ()
2818 {
2819     PopDown(CommentDlg);
2820 }
2821
2822 static char *openName;
2823 FILE *openFP;
2824
2825 void
2826 DelayedLoad ()
2827 {
2828   (void) (*fileProc)(openFP, 0, openName);
2829 }
2830
2831 void
2832 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
2833 {
2834     fileProc = proc;            /* I can't see a way not */
2835     fileOpenMode = openMode;    /*   to use globals here */
2836     {   // [HGM] use file-selector dialog stolen from Ghostview
2837         int index; // this is not supported yet
2838         Browse(BoardWindow, label, (def[0] ? def : NULL), filter, False, openMode, &openName, &openFP);
2839     }
2840 }
2841
2842
2843 /* Disable all user input other than deleting the window */
2844 static int frozen = 0;
2845
2846 void
2847 FreezeUI ()
2848 {
2849   if (frozen) return;
2850   /* Grab by a widget that doesn't accept input */
2851   XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
2852   frozen = 1;
2853 }
2854
2855 /* Undo a FreezeUI */
2856 void
2857 ThawUI ()
2858 {
2859   if (!frozen) return;
2860   XtRemoveGrab(optList[W_MESSG].handle);
2861   frozen = 0;
2862 }
2863
2864 void
2865 ModeHighlight ()
2866 {
2867     Arg args[16];
2868     static int oldPausing = FALSE;
2869     static GameMode oldmode = (GameMode) -1;
2870     char *wname;
2871
2872     if (!boardWidget || !XtIsRealized(boardWidget)) return;
2873
2874     if (pausing != oldPausing) {
2875         oldPausing = pausing;
2876         MarkMenuItem("Mode.Pause", pausing);
2877
2878         if (appData.showButtonBar) {
2879           /* Always toggle, don't set.  Previous code messes up when
2880              invoked while the button is pressed, as releasing it
2881              toggles the state again. */
2882           {
2883             Pixel oldbg, oldfg;
2884             XtSetArg(args[0], XtNbackground, &oldbg);
2885             XtSetArg(args[1], XtNforeground, &oldfg);
2886             XtGetValues(optList[W_PAUSE].handle,
2887                         args, 2);
2888             XtSetArg(args[0], XtNbackground, oldfg);
2889             XtSetArg(args[1], XtNforeground, oldbg);
2890           }
2891           XtSetValues(optList[W_PAUSE].handle, args, 2);
2892         }
2893     }
2894
2895     wname = ModeToWidgetName(oldmode);
2896     if (wname != NULL) {
2897         MarkMenuItem(wname, False);
2898     }
2899     wname = ModeToWidgetName(gameMode);
2900     if (wname != NULL) {
2901         MarkMenuItem(wname, True);
2902     }
2903     oldmode = gameMode;
2904     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
2905
2906     /* Maybe all the enables should be handled here, not just this one */
2907     EnableMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
2908 }
2909
2910
2911 /*
2912  * Button/menu procedures
2913  */
2914
2915 /* this variable is shared between CopyPositionProc and SendPositionSelection */
2916 char *selected_fen_position=NULL;
2917
2918 Boolean
2919 SendPositionSelection (Widget w, Atom *selection, Atom *target,
2920                        Atom *type_return, XtPointer *value_return,
2921                        unsigned long *length_return, int *format_return)
2922 {
2923   char *selection_tmp;
2924
2925 //  if (!selected_fen_position) return False; /* should never happen */
2926   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
2927    if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
2928     FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
2929     long len;
2930     size_t count;
2931     if (f == NULL) return False;
2932     fseek(f, 0, 2);
2933     len = ftell(f);
2934     rewind(f);
2935     selection_tmp = XtMalloc(len + 1);
2936     count = fread(selection_tmp, 1, len, f);
2937     fclose(f);
2938     if (len != count) {
2939       XtFree(selection_tmp);
2940       return False;
2941     }
2942     selection_tmp[len] = NULLCHAR;
2943    } else {
2944     /* note: since no XtSelectionDoneProc was registered, Xt will
2945      * automatically call XtFree on the value returned.  So have to
2946      * make a copy of it allocated with XtMalloc */
2947     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
2948     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
2949    }
2950
2951     *value_return=selection_tmp;
2952     *length_return=strlen(selection_tmp);
2953     *type_return=*target;
2954     *format_return = 8; /* bits per byte */
2955     return True;
2956   } else if (*target == XA_TARGETS(xDisplay)) {
2957     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
2958     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
2959     targets_tmp[1] = XA_STRING;
2960     *value_return = targets_tmp;
2961     *type_return = XA_ATOM;
2962     *length_return = 2;
2963 #if 0
2964     // This code leads to a read of value_return out of bounds on 64-bit systems.
2965     // Other code which I have seen always sets *format_return to 32 independent of
2966     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
2967     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
2968     *format_return = 8 * sizeof(Atom);
2969     if (*format_return > 32) {
2970       *length_return *= *format_return / 32;
2971       *format_return = 32;
2972     }
2973 #else
2974     *format_return = 32;
2975 #endif
2976     return True;
2977   } else {
2978     return False;
2979   }
2980 }
2981
2982 /* note: when called from menu all parameters are NULL, so no clue what the
2983  * Widget which was clicked on was, or what the click event was
2984  */
2985 void
2986 CopySomething (char *src)
2987 {
2988     selected_fen_position = src;
2989     /*
2990      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2991      * have a notion of a position that is selected but not copied.
2992      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2993      */
2994     XtOwnSelection(menuBarWidget, XA_PRIMARY,
2995                    CurrentTime,
2996                    SendPositionSelection,
2997                    NULL/* lose_ownership_proc */ ,
2998                    NULL/* transfer_done_proc */);
2999     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3000                    CurrentTime,
3001                    SendPositionSelection,
3002                    NULL/* lose_ownership_proc */ ,
3003                    NULL/* transfer_done_proc */);
3004 }
3005
3006 /* function called when the data to Paste is ready */
3007 static void
3008 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3009                  Atom *type, XtPointer value, unsigned long *len, int *format)
3010 {
3011   char *fenstr=value;
3012   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3013   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3014   EditPositionPasteFEN(fenstr);
3015   XtFree(value);
3016 }
3017
3018 /* called when Paste Position button is pressed,
3019  * all parameters will be NULL */
3020 void
3021 PastePositionProc ()
3022 {
3023     XtGetSelectionValue(menuBarWidget,
3024       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3025       /* (XtSelectionCallbackProc) */ PastePositionCB,
3026       NULL, /* client_data passed to PastePositionCB */
3027
3028       /* better to use the time field from the event that triggered the
3029        * call to this function, but that isn't trivial to get
3030        */
3031       CurrentTime
3032     );
3033     return;
3034 }
3035
3036 /* note: when called from menu all parameters are NULL, so no clue what the
3037  * Widget which was clicked on was, or what the click event was
3038  */
3039 /* function called when the data to Paste is ready */
3040 static void
3041 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3042              Atom *type, XtPointer value, unsigned long *len, int *format)
3043 {
3044   FILE* f;
3045   if (value == NULL || *len == 0) {
3046     return; /* nothing had been selected to copy */
3047   }
3048   f = fopen(gamePasteFilename, "w");
3049   if (f == NULL) {
3050     DisplayError(_("Can't open temp file"), errno);
3051     return;
3052   }
3053   fwrite(value, 1, *len, f);
3054   fclose(f);
3055   XtFree(value);
3056   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3057 }
3058
3059 /* called when Paste Game button is pressed,
3060  * all parameters will be NULL */
3061 void
3062 PasteGameProc ()
3063 {
3064     XtGetSelectionValue(menuBarWidget,
3065       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3066       /* (XtSelectionCallbackProc) */ PasteGameCB,
3067       NULL, /* client_data passed to PasteGameCB */
3068
3069       /* better to use the time field from the event that triggered the
3070        * call to this function, but that isn't trivial to get
3071        */
3072       CurrentTime
3073     );
3074     return;
3075 }
3076
3077
3078 void
3079 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3080 {
3081     QuitProc();
3082 }
3083
3084 int
3085 ShiftKeys ()
3086 {   // bassic primitive for determining if modifier keys are pressed
3087     long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3088     char keys[32];
3089     int i,j,  k=0;
3090     XQueryKeymap(xDisplay,keys);
3091     for(i=0; i<6; i++) {
3092         k <<= 1;
3093         j = XKeysymToKeycode(xDisplay, codes[i]);
3094         k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3095     }
3096     return k;
3097 }
3098
3099 static void
3100 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3101 {
3102     char buf[10];
3103     KeySym sym;
3104     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3105     if ( n == 1 && *buf >= 32 // printable
3106          && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3107         ) BoxAutoPopUp (buf);
3108 }
3109
3110 static void
3111 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3112 {   // [HGM] input: let up-arrow recall previous line from history
3113     IcsKey(1);
3114 }
3115
3116 static void
3117 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3118 {   // [HGM] input: let down-arrow recall next line from history
3119     IcsKey(-1);
3120 }
3121
3122 static void
3123 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3124 {
3125     IcsKey(0);
3126 }
3127
3128 void
3129 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3130 {
3131         if (!TempBackwardActive) {
3132                 TempBackwardActive = True;
3133                 BackwardEvent();
3134         }
3135 }
3136
3137 void
3138 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3139 {
3140         /* Check to see if triggered by a key release event for a repeating key.
3141          * If so the next queued event will be a key press of the same key at the same time */
3142         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3143                 XEvent next;
3144                 XPeekEvent(xDisplay, &next);
3145                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3146                         next.xkey.keycode == event->xkey.keycode)
3147                                 return;
3148         }
3149     ForwardEvent();
3150         TempBackwardActive = False;
3151 }
3152
3153 void
3154 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3155 {   // called as key binding
3156     char buf[MSG_SIZ];
3157     String name;
3158     if (nprms && *nprms > 0)
3159       name = prms[0];
3160     else
3161       name = "xboard";
3162     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3163     system(buf);
3164 }
3165
3166 void
3167 SetWindowTitle (char *text, char *title, char *icon)
3168 {
3169     Arg args[16];
3170     int i;
3171     if (appData.titleInWindow) {
3172         i = 0;
3173         XtSetArg(args[i], XtNlabel, text);   i++;
3174         XtSetValues(titleWidget, args, i);
3175     }
3176     i = 0;
3177     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
3178     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
3179     XtSetValues(shellWidget, args, i);
3180     XSync(xDisplay, False);
3181 }
3182
3183
3184 static int
3185 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3186 {
3187     return 0;
3188 }
3189
3190 void
3191 DisplayIcsInteractionTitle (String message)
3192 {
3193   if (oldICSInteractionTitle == NULL) {
3194     /* Magic to find the old window title, adapted from vim */
3195     char *wina = getenv("WINDOWID");
3196     if (wina != NULL) {
3197       Window win = (Window) atoi(wina);
3198       Window root, parent, *children;
3199       unsigned int nchildren;
3200       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3201       for (;;) {
3202         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3203         if (!XQueryTree(xDisplay, win, &root, &parent,
3204                         &children, &nchildren)) break;
3205         if (children) XFree((void *)children);
3206         if (parent == root || parent == 0) break;
3207         win = parent;
3208       }
3209       XSetErrorHandler(oldHandler);
3210     }
3211     if (oldICSInteractionTitle == NULL) {
3212       oldICSInteractionTitle = "xterm";
3213     }
3214   }
3215   printf("\033]0;%s\007", message);
3216   fflush(stdout);
3217 }
3218
3219
3220 XtIntervalId delayedEventTimerXID = 0;
3221 DelayedEventCallback delayedEventCallback = 0;
3222
3223 void
3224 FireDelayedEvent ()
3225 {
3226     delayedEventTimerXID = 0;
3227     delayedEventCallback();
3228 }
3229
3230 void
3231 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3232 {
3233     if(delayedEventTimerXID && delayedEventCallback == cb)
3234         // [HGM] alive: replace, rather than add or flush identical event
3235         XtRemoveTimeOut(delayedEventTimerXID);
3236     delayedEventCallback = cb;
3237     delayedEventTimerXID =
3238       XtAppAddTimeOut(appContext, millisec,
3239                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3240 }
3241
3242 DelayedEventCallback
3243 GetDelayedEvent ()
3244 {
3245   if (delayedEventTimerXID) {
3246     return delayedEventCallback;
3247   } else {
3248     return NULL;
3249   }
3250 }
3251
3252 void
3253 CancelDelayedEvent ()
3254 {
3255   if (delayedEventTimerXID) {
3256     XtRemoveTimeOut(delayedEventTimerXID);
3257     delayedEventTimerXID = 0;
3258   }
3259 }
3260
3261 XtIntervalId loadGameTimerXID = 0;
3262
3263 int
3264 LoadGameTimerRunning ()
3265 {
3266     return loadGameTimerXID != 0;
3267 }
3268
3269 int
3270 StopLoadGameTimer ()
3271 {
3272     if (loadGameTimerXID != 0) {
3273         XtRemoveTimeOut(loadGameTimerXID);
3274         loadGameTimerXID = 0;
3275         return TRUE;
3276     } else {
3277         return FALSE;
3278     }
3279 }
3280
3281 void
3282 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3283 {
3284     loadGameTimerXID = 0;
3285     AutoPlayGameLoop();
3286 }
3287
3288 void
3289 StartLoadGameTimer (long millisec)
3290 {
3291     loadGameTimerXID =
3292       XtAppAddTimeOut(appContext, millisec,
3293                       (XtTimerCallbackProc) LoadGameTimerCallback,
3294                       (XtPointer) 0);
3295 }
3296
3297 XtIntervalId analysisClockXID = 0;
3298
3299 void
3300 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3301 {
3302     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3303          || appData.icsEngineAnalyze) { // [DM]
3304         AnalysisPeriodicEvent(0);
3305         StartAnalysisClock();
3306     }
3307 }
3308
3309 void
3310 StartAnalysisClock ()
3311 {
3312     analysisClockXID =
3313       XtAppAddTimeOut(appContext, 2000,
3314                       (XtTimerCallbackProc) AnalysisClockCallback,
3315                       (XtPointer) 0);
3316 }
3317
3318 XtIntervalId clockTimerXID = 0;
3319
3320 int
3321 ClockTimerRunning ()
3322 {
3323     return clockTimerXID != 0;
3324 }
3325
3326 int
3327 StopClockTimer ()
3328 {
3329     if (clockTimerXID != 0) {
3330         XtRemoveTimeOut(clockTimerXID);
3331         clockTimerXID = 0;
3332         return TRUE;
3333     } else {
3334         return FALSE;
3335     }
3336 }
3337
3338 void
3339 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3340 {
3341     clockTimerXID = 0;
3342     DecrementClocks();
3343 }
3344
3345 void
3346 StartClockTimer (long millisec)
3347 {
3348     clockTimerXID =
3349       XtAppAddTimeOut(appContext, millisec,
3350                       (XtTimerCallbackProc) ClockTimerCallback,
3351                       (XtPointer) 0);
3352 }
3353
3354 void
3355 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3356 {
3357     char buf[MSG_SIZ];
3358     Arg args[16];
3359     Widget w = (Widget) opt->handle;
3360
3361     /* check for low time warning */
3362     Pixel foregroundOrWarningColor = timerForegroundPixel;
3363
3364     if (timer > 0 &&
3365         appData.lowTimeWarning &&
3366         (timer / 1000) < appData.icsAlarmTime)
3367       foregroundOrWarningColor = lowTimeWarningColor;
3368
3369     if (appData.clockMode) {
3370       snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
3371       XtSetArg(args[0], XtNlabel, buf);
3372     } else {
3373       snprintf(buf, MSG_SIZ, "%s  ", color);
3374       XtSetArg(args[0], XtNlabel, buf);
3375     }
3376
3377     if (highlight) {
3378
3379         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3380         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3381     } else {
3382         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3383         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3384     }
3385
3386     XtSetValues(w, args, 3);
3387 }
3388
3389 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3390
3391 void
3392 SetClockIcon (int color)
3393 {
3394     Arg args[16];
3395     Pixmap pm = *clockIcons[color];
3396     if (iconPixmap != pm) {
3397         iconPixmap = pm;
3398         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3399         XtSetValues(shellWidget, args, 1);
3400     }
3401 }
3402
3403 void
3404 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3405 {
3406     InputSource *is = (InputSource *) closure;
3407     int count;
3408     int error;
3409     char *p, *q;
3410
3411     if (is->lineByLine) {
3412         count = read(is->fd, is->unused,
3413                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3414         if (count <= 0) {
3415             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3416             return;
3417         }
3418         is->unused += count;
3419         p = is->buf;
3420         while (p < is->unused) {
3421             q = memchr(p, '\n', is->unused - p);
3422             if (q == NULL) break;
3423             q++;
3424             (is->func)(is, is->closure, p, q - p, 0);
3425             p = q;
3426         }
3427         q = is->buf;
3428         while (p < is->unused) {
3429             *q++ = *p++;
3430         }
3431         is->unused = q;
3432     } else {
3433         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3434         if (count == -1)
3435           error = errno;
3436         else
3437           error = 0;
3438         (is->func)(is, is->closure, is->buf, count, error);
3439     }
3440 }
3441
3442 InputSourceRef
3443 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3444 {
3445     InputSource *is;
3446     ChildProc *cp = (ChildProc *) pr;
3447
3448     is = (InputSource *) calloc(1, sizeof(InputSource));
3449     is->lineByLine = lineByLine;
3450     is->func = func;
3451     if (pr == NoProc) {
3452         is->kind = CPReal;
3453         is->fd = fileno(stdin);
3454     } else {
3455         is->kind = cp->kind;
3456         is->fd = cp->fdFrom;
3457     }
3458     if (lineByLine) {
3459         is->unused = is->buf;
3460     }
3461
3462     is->xid = XtAppAddInput(appContext, is->fd,
3463                             (XtPointer) (XtInputReadMask),
3464                             (XtInputCallbackProc) DoInputCallback,
3465                             (XtPointer) is);
3466     is->closure = closure;
3467     return (InputSourceRef) is;
3468 }
3469
3470 void
3471 RemoveInputSource (InputSourceRef isr)
3472 {
3473     InputSource *is = (InputSource *) isr;
3474
3475     if (is->xid == 0) return;
3476     XtRemoveInput(is->xid);
3477     is->xid = 0;
3478 }
3479
3480 /****   Animation code by Hugh Fisher, DCS, ANU. ****/
3481
3482 /*      Masks for XPM pieces. Black and white pieces can have
3483         different shapes, but in the interest of retaining my
3484         sanity pieces must have the same outline on both light
3485         and dark squares, and all pieces must use the same
3486         background square colors/images.                */
3487
3488 static int xpmDone = 0;
3489 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3490 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3491
3492 static void
3493 CreateAnimMasks (int pieceDepth)
3494 {
3495   ChessSquare   piece;
3496   Pixmap        buf;
3497   GC            bufGC, maskGC;
3498   int           kind, n;
3499   unsigned long plane;
3500   XGCValues     values;
3501
3502   /* Need a bitmap just to get a GC with right depth */
3503   buf = XCreatePixmap(xDisplay, xBoardWindow,
3504                         8, 8, 1);
3505   values.foreground = 1;
3506   values.background = 0;
3507   /* Don't use XtGetGC, not read only */
3508   maskGC = XCreateGC(xDisplay, buf,
3509                     GCForeground | GCBackground, &values);
3510   XFreePixmap(xDisplay, buf);
3511
3512   buf = XCreatePixmap(xDisplay, xBoardWindow,
3513                       squareSize, squareSize, pieceDepth);
3514   values.foreground = XBlackPixel(xDisplay, xScreen);
3515   values.background = XWhitePixel(xDisplay, xScreen);
3516   bufGC = XCreateGC(xDisplay, buf,
3517                     GCForeground | GCBackground, &values);
3518
3519   for (piece = WhitePawn; piece <= BlackKing; piece++) {
3520     /* Begin with empty mask */
3521     if(!xpmDone) // [HGM] pieces: keep using existing
3522     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3523                                  squareSize, squareSize, 1);
3524     XSetFunction(xDisplay, maskGC, GXclear);
3525     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3526                    0, 0, squareSize, squareSize);
3527
3528     /* Take a copy of the piece */
3529     if (White(piece))
3530       kind = 0;
3531     else
3532       kind = 2;
3533     XSetFunction(xDisplay, bufGC, GXcopy);
3534     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3535               buf, bufGC,
3536               0, 0, squareSize, squareSize, 0, 0);
3537
3538     /* XOR the background (light) over the piece */
3539     XSetFunction(xDisplay, bufGC, GXxor);
3540     if (useImageSqs)
3541       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3542                 0, 0, squareSize, squareSize, 0, 0);
3543     else {
3544       XSetForeground(xDisplay, bufGC, lightSquareColor);
3545       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3546     }
3547
3548     /* We now have an inverted piece image with the background
3549        erased. Construct mask by just selecting all the non-zero
3550        pixels - no need to reconstruct the original image.      */
3551     XSetFunction(xDisplay, maskGC, GXor);
3552     plane = 1;
3553     /* Might be quicker to download an XImage and create bitmap
3554        data from it rather than this N copies per piece, but it
3555        only takes a fraction of a second and there is a much
3556        longer delay for loading the pieces.             */
3557     for (n = 0; n < pieceDepth; n ++) {
3558       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3559                  0, 0, squareSize, squareSize,
3560                  0, 0, plane);
3561       plane = plane << 1;
3562     }
3563   }
3564   /* Clean up */
3565   XFreePixmap(xDisplay, buf);
3566   XFreeGC(xDisplay, bufGC);
3567   XFreeGC(xDisplay, maskGC);
3568 }
3569
3570 static void
3571 InitAnimState (AnimNr anr, XWindowAttributes *info)
3572 {
3573   XtGCMask  mask;
3574   XGCValues values;
3575
3576   /* Each buffer is square size, same depth as window */
3577   animBufs[anr+4] = xBoardWindow;
3578   animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3579                         squareSize, squareSize, info->depth);
3580   animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3581                         squareSize, squareSize, info->depth);
3582
3583   /* Create a plain GC for blitting */
3584   mask = GCForeground | GCBackground | GCFunction |
3585          GCPlaneMask | GCGraphicsExposures;
3586   values.foreground = XBlackPixel(xDisplay, xScreen);
3587   values.background = XWhitePixel(xDisplay, xScreen);
3588   values.function   = GXcopy;
3589   values.plane_mask = AllPlanes;
3590   values.graphics_exposures = False;
3591   animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3592
3593   /* Piece will be copied from an existing context at
3594      the start of each new animation/drag. */
3595   animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3596
3597   /* Outline will be a read-only copy of an existing */
3598   animGCs[anr+4] = None;
3599 }
3600
3601 void
3602 CreateAnimVars ()
3603 {
3604   XWindowAttributes info;
3605
3606   if (xpmDone && gameInfo.variant == oldVariant) return;
3607   if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3608   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3609
3610   InitAnimState(Game, &info);
3611   InitAnimState(Player, &info);
3612
3613   /* For XPM pieces, we need bitmaps to use as masks. */
3614   if (useImages)
3615     CreateAnimMasks(info.depth), xpmDone = 1;
3616 }
3617
3618 #ifndef HAVE_USLEEP
3619
3620 static Boolean frameWaiting;
3621
3622 static RETSIGTYPE
3623 FrameAlarm (int sig)
3624 {
3625   frameWaiting = False;
3626   /* In case System-V style signals.  Needed?? */
3627   signal(SIGALRM, FrameAlarm);
3628 }
3629
3630 void
3631 FrameDelay (int time)
3632 {
3633   struct itimerval delay;
3634
3635   XSync(xDisplay, False);
3636
3637   if (time > 0) {
3638     frameWaiting = True;
3639     signal(SIGALRM, FrameAlarm);
3640     delay.it_interval.tv_sec =
3641       delay.it_value.tv_sec = time / 1000;
3642     delay.it_interval.tv_usec =
3643       delay.it_value.tv_usec = (time % 1000) * 1000;
3644     setitimer(ITIMER_REAL, &delay, NULL);
3645     while (frameWaiting) pause();
3646     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3647     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3648     setitimer(ITIMER_REAL, &delay, NULL);
3649   }
3650 }
3651
3652 #else
3653
3654 void
3655 FrameDelay (int time)
3656 {
3657   XSync(xDisplay, False);
3658   if (time > 0)
3659     usleep(time * 1000);
3660 }
3661
3662 #endif
3663
3664 static void
3665 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3666 {
3667   GC source;
3668
3669   /* Bitmap for piece being moved. */
3670   if (appData.monoMode) {
3671       *mask = *pieceToSolid(piece);
3672   } else if (useImages) {
3673 #if HAVE_LIBXPM
3674       *mask = xpmMask[piece];
3675 #else
3676       *mask = ximMaskPm[piece];
3677 #endif
3678   } else {
3679       *mask = *pieceToSolid(piece);
3680   }
3681
3682   /* GC for piece being moved. Square color doesn't matter, but
3683      since it gets modified we make a copy of the original. */
3684   if (White(piece)) {
3685     if (appData.monoMode)
3686       source = bwPieceGC;
3687     else
3688       source = wlPieceGC;
3689   } else {
3690     if (appData.monoMode)
3691       source = wbPieceGC;
3692     else
3693       source = blPieceGC;
3694   }
3695   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3696
3697   /* Outline only used in mono mode and is not modified */
3698   if (White(piece))
3699     *outline = bwPieceGC;
3700   else
3701     *outline = wbPieceGC;
3702 }
3703
3704 static void
3705 OverlayPiece (ChessSquare piece, GC clip, GC outline,  Drawable dest)
3706 {
3707   int   kind;
3708
3709   if (!useImages) {
3710     /* Draw solid rectangle which will be clipped to shape of piece */
3711     XFillRectangle(xDisplay, dest, clip,
3712                    0, 0, squareSize, squareSize);
3713     if (appData.monoMode)
3714       /* Also draw outline in contrasting color for black
3715          on black / white on white cases                */
3716       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3717                  0, 0, squareSize, squareSize, 0, 0, 1);
3718   } else {
3719     /* Copy the piece */
3720     if (White(piece))
3721       kind = 0;
3722     else
3723       kind = 2;
3724     if(appData.upsideDown && flipView) kind ^= 2;
3725     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3726               dest, clip,
3727               0, 0, squareSize, squareSize,
3728               0, 0);
3729   }
3730 }
3731
3732 void
3733 InsertPiece (AnimNr anr, ChessSquare piece)
3734 {
3735   OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
3736 }
3737
3738 void
3739 DrawBlank (AnimNr anr, int x, int y, int startColor)
3740 {
3741     BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
3742 }
3743
3744 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
3745                  int srcX, int srcY, int width, int height, int destX, int destY)
3746 {
3747     XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
3748                 srcX, srcY, width, height, destX, destY);
3749 }
3750
3751 void
3752 SetDragPiece (AnimNr anr, ChessSquare piece)
3753 {
3754   Pixmap mask;
3755   /* The piece will be drawn using its own bitmap as a matte    */
3756   SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
3757   XSetClipMask(xDisplay, animGCs[anr+2], mask);
3758 }
3759
3760 /* [AS] Arrow highlighting support */
3761
3762 void
3763 DrawPolygon (Pnt arrow[], int nr)
3764 {
3765     XPoint pts[10];
3766     int i;
3767     for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
3768     XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
3769     if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
3770 }
3771
3772 void
3773 UpdateLogos (int displ)
3774 {
3775     return; // no logos in XBoard yet
3776 }
3777