Improve arrow drawing
[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;
940
941     if(!formWidget) return;
942
943     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
944     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
945     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
946
947   if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
948
949     oldWidth = boardWidth; oldHeight = boardHeight;
950     CreateGrid();
951
952     /*
953      * Inhibit shell resizing.
954      */
955     shellArgs[0].value = w = (XtArgVal) boardWidth + marginW ;
956     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
957     shellArgs[4].value = shellArgs[2].value = w;
958     shellArgs[5].value = shellArgs[3].value = h;
959     XtSetValues(shellWidget, &shellArgs[0], 6);
960
961     XSync(xDisplay, False);
962     DelayedDrag();
963   }
964
965     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
966     // (only for xpm)
967
968   if(gameInfo.variant != oldVariant) { // and only if variant changed
969
970     if(useImages) {
971       for(i=0; i<4; i++) {
972         int p;
973         for(p=0; p<=(int)WhiteKing; p++)
974            xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
975         if(gameInfo.variant == VariantShogi) {
976            xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
977            xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
978            xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
979            xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
980            xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
981         }
982 #ifdef GOTHIC
983         if(gameInfo.variant == VariantGothic) {
984            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
985         }
986 #endif
987         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
988            xpmPieceBitmap[i][(int)WhiteAngel]    = xpmPieceBitmap2[i][(int)WhiteFalcon];
989            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
990         }
991 #if !HAVE_LIBXPM
992         // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
993         for(p=0; p<=(int)WhiteKing; p++)
994            ximMaskPm[p] = ximMaskPm2[p]; // defaults
995         if(gameInfo.variant == VariantShogi) {
996            ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
997            ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
998            ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
999            ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1000            ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1001         }
1002 #ifdef GOTHIC
1003         if(gameInfo.variant == VariantGothic) {
1004            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1005         }
1006 #endif
1007         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1008            ximMaskPm[(int)WhiteAngel]    = ximMaskPm2[(int)WhiteFalcon];
1009            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1010         }
1011 #endif
1012       }
1013     } else {
1014       for(i=0; i<2; i++) {
1015         int p;
1016         for(p=0; p<=(int)WhiteKing; p++)
1017            pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1018         if(gameInfo.variant == VariantShogi) {
1019            pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1020            pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1021            pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1022            pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1023            pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1024         }
1025 #ifdef GOTHIC
1026         if(gameInfo.variant == VariantGothic) {
1027            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1028         }
1029 #endif
1030         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1031            pieceBitmap[i][(int)WhiteAngel]    = pieceBitmap2[i][(int)WhiteFalcon];
1032            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1033         }
1034       }
1035     }
1036     oldMono = -10; // kludge to force recreation of animation masks
1037     oldVariant = gameInfo.variant;
1038   }
1039 #if HAVE_LIBXPM
1040   if(appData.monoMode != oldMono)
1041     CreateAnimVars();
1042 #endif
1043   oldMono = appData.monoMode;
1044 }
1045
1046 static int
1047 MakeOneColor (char *name, Pixel *color)
1048 {
1049     XrmValue vFrom, vTo;
1050     if (!appData.monoMode) {
1051         vFrom.addr = (caddr_t) name;
1052         vFrom.size = strlen(name);
1053         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1054         if (vTo.addr == NULL) {
1055           appData.monoMode = True;
1056           return True;
1057         } else {
1058           *color = *(Pixel *) vTo.addr;
1059         }
1060     }
1061     return False;
1062 }
1063
1064 static int
1065 MakeColors ()
1066 {   // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1067     int forceMono = False;
1068
1069     forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1070     forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1071     forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1072     forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1073     forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1074     forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1075     if (appData.lowTimeWarning)
1076         forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1077     if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1078     if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1079
1080     return forceMono;
1081 }
1082
1083 static void
1084 CreateAnyPieces ()
1085 {   // [HGM] taken out of main
1086 #if HAVE_LIBXPM
1087     if (appData.monoMode && // [HGM] no sense to go on to certain doom
1088        (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1089             appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1090
1091     if (appData.bitmapDirectory[0] != NULLCHAR) {
1092       CreatePieces();
1093     } else {
1094       CreateXPMPieces();
1095       CreateXPMBoard(appData.liteBackTextureFile, 1);
1096       CreateXPMBoard(appData.darkBackTextureFile, 0);
1097     }
1098 #else
1099     CreateXIMPieces();
1100     /* Create regular pieces */
1101     if (!useImages) CreatePieces();
1102 #endif
1103 }
1104
1105 void
1106 InitDrawingParams ()
1107 {
1108     MakeColors(); CreateGCs(True);
1109     CreateAnyPieces();
1110 }
1111
1112 void
1113 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1114 {   // determine what fonts to use, and create them
1115     XrmValue vFrom, vTo;
1116     XrmDatabase xdb;
1117
1118     if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1119         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1120     if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1121         appData.font = fontTable[MESSAGE_FONT][squareSize];
1122     if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1123         appData.coordFont = fontTable[COORD_FONT][squareSize];
1124
1125 #if ENABLE_NLS
1126     appData.font = InsertPxlSize(appData.font, fontPxlSize);
1127     appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1128     appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1129     fontSet = CreateFontSet(appData.font);
1130     clockFontSet = CreateFontSet(appData.clockFont);
1131     {
1132       /* For the coordFont, use the 0th font of the fontset. */
1133       XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1134       XFontStruct **font_struct_list;
1135       XFontSetExtents *fontSize;
1136       char **font_name_list;
1137       XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1138       coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1139       coordFontStruct = XQueryFont(xDisplay, coordFontID);
1140       fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1141       textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1142     }
1143 #else
1144     appData.font = FindFont(appData.font, fontPxlSize);
1145     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1146     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1147     clockFontID = XLoadFont(xDisplay, appData.clockFont);
1148     clockFontStruct = XQueryFont(xDisplay, clockFontID);
1149     coordFontID = XLoadFont(xDisplay, appData.coordFont);
1150     coordFontStruct = XQueryFont(xDisplay, coordFontID);
1151     // textHeight in !NLS mode!
1152 #endif
1153     countFontID = coordFontID;  // [HGM] holdings
1154     countFontStruct = coordFontStruct;
1155
1156     xdb = XtDatabase(xDisplay);
1157 #if ENABLE_NLS
1158     XrmPutLineResource(&xdb, "*international: True");
1159     vTo.size = sizeof(XFontSet);
1160     vTo.addr = (XtPointer) &fontSet;
1161     XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1162 #else
1163     XrmPutStringResource(&xdb, "*font", appData.font);
1164 #endif
1165 }
1166
1167 int
1168 main (int argc, char **argv)
1169 {
1170     int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1171     XSetWindowAttributes window_attributes;
1172     Arg args[16];
1173     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1174     XtGeometryResult gres;
1175     char *p;
1176     int forceMono = False;
1177
1178     srandom(time(0)); // [HGM] book: make random truly random
1179
1180     setbuf(stdout, NULL);
1181     setbuf(stderr, NULL);
1182     debugFP = stderr;
1183
1184     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1185         printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1186         exit(0);
1187     }
1188
1189     programName = strrchr(argv[0], '/');
1190     if (programName == NULL)
1191       programName = argv[0];
1192     else
1193       programName++;
1194
1195 #ifdef ENABLE_NLS
1196     XtSetLanguageProc(NULL, NULL, NULL);
1197     if (appData.debugMode) {
1198       fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1199     }
1200
1201     bindtextdomain(PACKAGE, LOCALEDIR);
1202     textdomain(PACKAGE);
1203 #endif
1204
1205     appData.boardSize = "";
1206     InitAppData(ConvertToLine(argc, argv));
1207     p = getenv("HOME");
1208     if (p == NULL) p = "/tmp";
1209     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1210     gameCopyFilename = (char*) malloc(i);
1211     gamePasteFilename = (char*) malloc(i);
1212     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1213     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1214
1215     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1216         static char buf[MSG_SIZ];
1217         EscapeExpand(buf, appData.firstInitString);
1218         appData.firstInitString = strdup(buf);
1219         EscapeExpand(buf, appData.secondInitString);
1220         appData.secondInitString = strdup(buf);
1221         EscapeExpand(buf, appData.firstComputerString);
1222         appData.firstComputerString = strdup(buf);
1223         EscapeExpand(buf, appData.secondComputerString);
1224         appData.secondComputerString = strdup(buf);
1225     }
1226
1227     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1228         chessDir = ".";
1229     } else {
1230         if (chdir(chessDir) != 0) {
1231             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1232             perror(chessDir);
1233             exit(1);
1234         }
1235     }
1236
1237     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1238         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1239         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
1240            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1241            exit(errno);
1242         }
1243         setbuf(debugFP, NULL);
1244     }
1245
1246     /* [HGM,HR] make sure board size is acceptable */
1247     if(appData.NrFiles > BOARD_FILES ||
1248        appData.NrRanks > BOARD_RANKS   )
1249          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1250
1251 #if !HIGHDRAG
1252     /* This feature does not work; animation needs a rewrite */
1253     appData.highlightDragging = FALSE;
1254 #endif
1255     InitBackEnd1();
1256
1257         gameInfo.variant = StringToVariant(appData.variant);
1258         InitPosition(FALSE);
1259
1260     shellWidget =
1261       XtAppInitialize(&appContext, "XBoard", shellOptions,
1262                       XtNumber(shellOptions),
1263                       &argc, argv, xboardResources, NULL, 0);
1264
1265     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1266                               clientResources, XtNumber(clientResources),
1267                               NULL, 0);
1268
1269     xDisplay = XtDisplay(shellWidget);
1270     xScreen = DefaultScreen(xDisplay);
1271     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1272
1273     /*
1274      * determine size, based on supplied or remembered -size, or screen size
1275      */
1276     if (isdigit(appData.boardSize[0])) {
1277         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1278                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1279                    &fontPxlSize, &smallLayout, &tinyLayout);
1280         if (i == 0) {
1281             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1282                     programName, appData.boardSize);
1283             exit(2);
1284         }
1285         if (i < 7) {
1286             /* Find some defaults; use the nearest known size */
1287             SizeDefaults *szd, *nearest;
1288             int distance = 99999;
1289             nearest = szd = sizeDefaults;
1290             while (szd->name != NULL) {
1291                 if (abs(szd->squareSize - squareSize) < distance) {
1292                     nearest = szd;
1293                     distance = abs(szd->squareSize - squareSize);
1294                     if (distance == 0) break;
1295                 }
1296                 szd++;
1297             }
1298             if (i < 2) lineGap = nearest->lineGap;
1299             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1300             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1301             if (i < 5) fontPxlSize = nearest->fontPxlSize;
1302             if (i < 6) smallLayout = nearest->smallLayout;
1303             if (i < 7) tinyLayout = nearest->tinyLayout;
1304         }
1305     } else {
1306         SizeDefaults *szd = sizeDefaults;
1307         if (*appData.boardSize == NULLCHAR) {
1308             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1309                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1310               szd++;
1311             }
1312             if (szd->name == NULL) szd--;
1313             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1314         } else {
1315             while (szd->name != NULL &&
1316                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1317             if (szd->name == NULL) {
1318                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1319                         programName, appData.boardSize);
1320                 exit(2);
1321             }
1322         }
1323         squareSize = szd->squareSize;
1324         lineGap = szd->lineGap;
1325         clockFontPxlSize = szd->clockFontPxlSize;
1326         coordFontPxlSize = szd->coordFontPxlSize;
1327         fontPxlSize = szd->fontPxlSize;
1328         smallLayout = szd->smallLayout;
1329         tinyLayout = szd->tinyLayout;
1330         // [HGM] font: use defaults from settings file if available and not overruled
1331     }
1332
1333     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1334     if (strlen(appData.pixmapDirectory) > 0) {
1335         p = ExpandPathName(appData.pixmapDirectory);
1336         if (!p) {
1337             fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1338                    appData.pixmapDirectory);
1339             exit(1);
1340         }
1341         if (appData.debugMode) {
1342           fprintf(stderr, _("\
1343 XBoard square size (hint): %d\n\
1344 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1345         }
1346         squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1347         if (appData.debugMode) {
1348             fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1349         }
1350     }
1351     defaultLineGap = lineGap;
1352     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1353
1354     /* [HR] height treated separately (hacked) */
1355     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1356     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1357
1358     /*
1359      * Determine what fonts to use.
1360      */
1361     InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1362
1363     /*
1364      * Detect if there are not enough colors available and adapt.
1365      */
1366     if (DefaultDepth(xDisplay, xScreen) <= 2) {
1367       appData.monoMode = True;
1368     }
1369
1370     forceMono = MakeColors();
1371
1372     if (forceMono) {
1373       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1374               programName);
1375         appData.monoMode = True;
1376     }
1377
1378     if (appData.monoMode && appData.debugMode) {
1379         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1380                 (unsigned long) XWhitePixel(xDisplay, xScreen),
1381                 (unsigned long) XBlackPixel(xDisplay, xScreen));
1382     }
1383
1384     ParseIcsTextColors();
1385
1386     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1387
1388     /*
1389      * widget hierarchy
1390      */
1391     if (tinyLayout) {
1392         layoutName = "tinyLayout";
1393     } else if (smallLayout) {
1394         layoutName = "smallLayout";
1395     } else {
1396         layoutName = "normalLayout";
1397     }
1398
1399     optList = BoardPopUp(squareSize, lineGap, (void*)
1400 #if ENABLE_NLS
1401                                                 &clockFontSet);
1402 #else
1403                                                 &clockFonStruct);
1404 #endif
1405     boardWidget      = optList[W_BOARD].handle;
1406     menuBarWidget    = optList[W_MENU].handle;
1407     dropMenu         = optList[W_DROP].handle;
1408     titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1409     formWidget  = XtParent(boardWidget);
1410     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1411     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1412     XtGetValues(optList[W_WHITE].handle, args, 2);
1413     if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1414       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1415       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1416       XtGetValues(optList[W_PAUSE].handle, args, 2);
1417     }
1418     AppendEnginesToMenu(appData.recentEngineList);
1419
1420     xBoardWindow = XtWindow(boardWidget);
1421
1422     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1423     //       not need to go into InitDrawingSizes().
1424
1425     /*
1426      * Create X checkmark bitmap and initialize option menu checks.
1427      */
1428     ReadBitmap(&xMarkPixmap, "checkmark.bm",
1429                checkmark_bits, checkmark_width, checkmark_height);
1430     InitMenuMarkers();
1431
1432     /*
1433      * Create an icon.
1434      */
1435     ReadBitmap(&wIconPixmap, "icon_white.bm",
1436                icon_white_bits, icon_white_width, icon_white_height);
1437     ReadBitmap(&bIconPixmap, "icon_black.bm",
1438                icon_black_bits, icon_black_width, icon_black_height);
1439     iconPixmap = wIconPixmap;
1440     i = 0;
1441     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
1442     XtSetValues(shellWidget, args, i);
1443
1444     /*
1445      * Create a cursor for the board widget.
1446      */
1447     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1448     XChangeWindowAttributes(xDisplay, xBoardWindow,
1449                             CWCursor, &window_attributes);
1450
1451     /*
1452      * Inhibit shell resizing.
1453      */
1454     shellArgs[0].value = (XtArgVal) &w;
1455     shellArgs[1].value = (XtArgVal) &h;
1456     XtGetValues(shellWidget, shellArgs, 2);
1457     shellArgs[4].value = shellArgs[2].value = w;
1458     shellArgs[5].value = shellArgs[3].value = h;
1459     XtSetValues(shellWidget, &shellArgs[2], 4);
1460     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1461     marginH =  h - boardHeight;
1462
1463     CatchDeleteWindow(shellWidget, "QuitProc");
1464
1465     CreateGCs(False);
1466     CreateGrid();
1467     CreateAnyPieces();
1468
1469     if (appData.animate || appData.animateDragging)
1470       CreateAnimVars();
1471
1472     XtAugmentTranslations(formWidget,
1473                           XtParseTranslationTable(globalTranslations));
1474
1475     XtAddEventHandler(formWidget, KeyPressMask, False,
1476                       (XtEventHandler) MoveTypeInProc, NULL);
1477     XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1478                       (XtEventHandler) EventProc, NULL);
1479
1480     /* [AS] Restore layout */
1481     if( wpMoveHistory.visible ) {
1482       HistoryPopUp();
1483     }
1484
1485     if( wpEvalGraph.visible )
1486       {
1487         EvalGraphPopUp();
1488       };
1489
1490     if( wpEngineOutput.visible ) {
1491       EngineOutputPopUp();
1492     }
1493
1494     InitBackEnd2();
1495
1496     if (errorExitStatus == -1) {
1497         if (appData.icsActive) {
1498             /* We now wait until we see "login:" from the ICS before
1499                sending the logon script (problems with timestamp otherwise) */
1500             /*ICSInitScript();*/
1501             if (appData.icsInputBox) ICSInputBoxPopUp();
1502         }
1503
1504     #ifdef SIGWINCH
1505     signal(SIGWINCH, TermSizeSigHandler);
1506     #endif
1507         signal(SIGINT, IntSigHandler);
1508         signal(SIGTERM, IntSigHandler);
1509         if (*appData.cmailGameName != NULLCHAR) {
1510             signal(SIGUSR1, CmailSigHandler);
1511         }
1512     }
1513
1514     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1515     InitPosition(TRUE);
1516 //    XtSetKeyboardFocus(shellWidget, formWidget);
1517     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1518
1519     XtAppMainLoop(appContext);
1520     if (appData.debugMode) fclose(debugFP); // [DM] debug
1521     return 0;
1522 }
1523
1524 RETSIGTYPE
1525 TermSizeSigHandler (int sig)
1526 {
1527     update_ics_width();
1528 }
1529
1530 RETSIGTYPE
1531 IntSigHandler (int sig)
1532 {
1533     ExitEvent(sig);
1534 }
1535
1536 RETSIGTYPE
1537 CmailSigHandler (int sig)
1538 {
1539     int dummy = 0;
1540     int error;
1541
1542     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
1543
1544     /* Activate call-back function CmailSigHandlerCallBack()             */
1545     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1546
1547     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1548 }
1549
1550 void
1551 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1552 {
1553     BoardToTop();
1554     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
1555 }
1556 /**** end signal code ****/
1557
1558
1559 #define Abs(n) ((n)<0 ? -(n) : (n))
1560
1561 #ifdef ENABLE_NLS
1562 char *
1563 InsertPxlSize (char *pattern, int targetPxlSize)
1564 {
1565     char *base_fnt_lst, strInt[12], *p, *q;
1566     int alternatives, i, len, strIntLen;
1567
1568     /*
1569      * Replace the "*" (if present) in the pixel-size slot of each
1570      * alternative with the targetPxlSize.
1571      */
1572     p = pattern;
1573     alternatives = 1;
1574     while ((p = strchr(p, ',')) != NULL) {
1575       alternatives++;
1576       p++;
1577     }
1578     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1579     strIntLen = strlen(strInt);
1580     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1581
1582     p = pattern;
1583     q = base_fnt_lst;
1584     while (alternatives--) {
1585       char *comma = strchr(p, ',');
1586       for (i=0; i<14; i++) {
1587         char *hyphen = strchr(p, '-');
1588         if (!hyphen) break;
1589         if (comma && hyphen > comma) break;
1590         len = hyphen + 1 - p;
1591         if (i == 7 && *p == '*' && len == 2) {
1592           p += len;
1593           memcpy(q, strInt, strIntLen);
1594           q += strIntLen;
1595           *q++ = '-';
1596         } else {
1597           memcpy(q, p, len);
1598           p += len;
1599           q += len;
1600         }
1601       }
1602       if (!comma) break;
1603       len = comma + 1 - p;
1604       memcpy(q, p, len);
1605       p += len;
1606       q += len;
1607     }
1608     strcpy(q, p);
1609
1610     return base_fnt_lst;
1611 }
1612
1613 XFontSet
1614 CreateFontSet (char *base_fnt_lst)
1615 {
1616     XFontSet fntSet;
1617     char **missing_list;
1618     int missing_count;
1619     char *def_string;
1620
1621     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1622                             &missing_list, &missing_count, &def_string);
1623     if (appData.debugMode) {
1624       int i, count;
1625       XFontStruct **font_struct_list;
1626       char **font_name_list;
1627       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1628       if (fntSet) {
1629         fprintf(debugFP, " got list %s, locale %s\n",
1630                 XBaseFontNameListOfFontSet(fntSet),
1631                 XLocaleOfFontSet(fntSet));
1632         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1633         for (i = 0; i < count; i++) {
1634           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1635         }
1636       }
1637       for (i = 0; i < missing_count; i++) {
1638         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1639       }
1640     }
1641     if (fntSet == NULL) {
1642       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1643       exit(2);
1644     }
1645     return fntSet;
1646 }
1647 #else // not ENABLE_NLS
1648 /*
1649  * Find a font that matches "pattern" that is as close as
1650  * possible to the targetPxlSize.  Prefer fonts that are k
1651  * pixels smaller to fonts that are k pixels larger.  The
1652  * pattern must be in the X Consortium standard format,
1653  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1654  * The return value should be freed with XtFree when no
1655  * longer needed.
1656  */
1657 char *
1658 FindFont (char *pattern, int targetPxlSize)
1659 {
1660     char **fonts, *p, *best, *scalable, *scalableTail;
1661     int i, j, nfonts, minerr, err, pxlSize;
1662
1663     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1664     if (nfonts < 1) {
1665         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1666                 programName, pattern);
1667         exit(2);
1668     }
1669
1670     best = fonts[0];
1671     scalable = NULL;
1672     minerr = 999999;
1673     for (i=0; i<nfonts; i++) {
1674         j = 0;
1675         p = fonts[i];
1676         if (*p != '-') continue;
1677         while (j < 7) {
1678             if (*p == NULLCHAR) break;
1679             if (*p++ == '-') j++;
1680         }
1681         if (j < 7) continue;
1682         pxlSize = atoi(p);
1683         if (pxlSize == 0) {
1684             scalable = fonts[i];
1685             scalableTail = p;
1686         } else {
1687             err = pxlSize - targetPxlSize;
1688             if (Abs(err) < Abs(minerr) ||
1689                 (minerr > 0 && err < 0 && -err == minerr)) {
1690                 best = fonts[i];
1691                 minerr = err;
1692             }
1693         }
1694     }
1695     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1696         /* If the error is too big and there is a scalable font,
1697            use the scalable font. */
1698         int headlen = scalableTail - scalable;
1699         p = (char *) XtMalloc(strlen(scalable) + 10);
1700         while (isdigit(*scalableTail)) scalableTail++;
1701         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1702     } else {
1703         p = (char *) XtMalloc(strlen(best) + 2);
1704         safeStrCpy(p, best, strlen(best)+1 );
1705     }
1706     if (appData.debugMode) {
1707         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
1708                 pattern, targetPxlSize, p);
1709     }
1710     XFreeFontNames(fonts);
1711     return p;
1712 }
1713 #endif
1714
1715 void
1716 DeleteGCs ()
1717 {   // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1718     // must be called before all non-first callse to CreateGCs()
1719     XtReleaseGC(shellWidget, highlineGC);
1720     XtReleaseGC(shellWidget, lightSquareGC);
1721     XtReleaseGC(shellWidget, darkSquareGC);
1722     XtReleaseGC(shellWidget, lineGC);
1723     if (appData.monoMode) {
1724         if (DefaultDepth(xDisplay, xScreen) == 1) {
1725             XtReleaseGC(shellWidget, wbPieceGC);
1726         } else {
1727             XtReleaseGC(shellWidget, bwPieceGC);
1728         }
1729     } else {
1730         XtReleaseGC(shellWidget, prelineGC);
1731         XtReleaseGC(shellWidget, wdPieceGC);
1732         XtReleaseGC(shellWidget, wlPieceGC);
1733         XtReleaseGC(shellWidget, bdPieceGC);
1734         XtReleaseGC(shellWidget, blPieceGC);
1735     }
1736 }
1737
1738 static GC
1739 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1740 {
1741     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1742       | GCBackground | GCFunction | GCPlaneMask;
1743     gc_values->foreground = foreground;
1744     gc_values->background = background;
1745     return XtGetGC(shellWidget, value_mask, gc_values);
1746 }
1747
1748 static void
1749 CreateGCs (int redo)
1750 {
1751     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1752       | GCBackground | GCFunction | GCPlaneMask;
1753     XGCValues gc_values;
1754     GC copyInvertedGC;
1755     Pixel white = XWhitePixel(xDisplay, xScreen);
1756     Pixel black = XBlackPixel(xDisplay, xScreen);
1757
1758     gc_values.plane_mask = AllPlanes;
1759     gc_values.line_width = lineGap;
1760     gc_values.line_style = LineSolid;
1761     gc_values.function = GXcopy;
1762
1763   if(redo) {
1764     DeleteGCs(); // called a second time; clean up old GCs first
1765   } else { // [HGM] grid and font GCs created on first call only
1766     coordGC = CreateOneGC(&gc_values, black, white);
1767     XSetFont(xDisplay, coordGC, coordFontID);
1768
1769     // [HGM] make font for holdings counts (white on black)
1770     countGC = CreateOneGC(&gc_values, white, black);
1771     XSetFont(xDisplay, countGC, countFontID);
1772   }
1773     lineGC = CreateOneGC(&gc_values, black, black);
1774
1775     if (appData.monoMode) {
1776
1777         highlineGC = CreateOneGC(&gc_values, white, white);
1778         lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1779         darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1780
1781         if (DefaultDepth(xDisplay, xScreen) == 1) {
1782             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1783             gc_values.function = GXcopyInverted;
1784             copyInvertedGC = CreateOneGC(&gc_values, black, white);
1785             gc_values.function = GXcopy;
1786             if (XBlackPixel(xDisplay, xScreen) == 1) {
1787                 bwPieceGC = darkSquareGC;
1788                 wbPieceGC = copyInvertedGC;
1789             } else {
1790                 bwPieceGC = copyInvertedGC;
1791                 wbPieceGC = lightSquareGC;
1792             }
1793         }
1794     } else {
1795
1796         highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1797         prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1798         lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1799         darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1800         wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1801         wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1802         bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1803         blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1804     }
1805 }
1806
1807 void
1808 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1809 {
1810     int x, y, w, h, p;
1811     FILE *fp;
1812     Pixmap temp;
1813     XGCValues   values;
1814     GC maskGC;
1815
1816     fp = fopen(filename, "rb");
1817     if (!fp) {
1818         fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1819         exit(1);
1820     }
1821
1822     w = fgetc(fp);
1823     h = fgetc(fp);
1824
1825     for (y=0; y<h; ++y) {
1826         for (x=0; x<h; ++x) {
1827             p = fgetc(fp);
1828
1829             switch (p) {
1830               case 0:
1831                 XPutPixel(xim, x, y, blackPieceColor);
1832                 if (xmask)
1833                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1834                 break;
1835               case 1:
1836                 XPutPixel(xim, x, y, darkSquareColor);
1837                 if (xmask)
1838                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1839                 break;
1840               case 2:
1841                 XPutPixel(xim, x, y, whitePieceColor);
1842                 if (xmask)
1843                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1844                 break;
1845               case 3:
1846                 XPutPixel(xim, x, y, lightSquareColor);
1847                 if (xmask)
1848                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1849                 break;
1850             }
1851         }
1852     }
1853
1854     fclose(fp);
1855
1856     /* create Pixmap of piece */
1857     *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1858                           w, h, xim->depth);
1859     XPutImage(xDisplay, *dest, lightSquareGC, xim,
1860               0, 0, 0, 0, w, h);
1861
1862     /* create Pixmap of clipmask
1863        Note: We assume the white/black pieces have the same
1864              outline, so we make only 6 masks. This is okay
1865              since the XPM clipmask routines do the same. */
1866     if (xmask) {
1867       temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1868                             w, h, xim->depth);
1869       XPutImage(xDisplay, temp, lightSquareGC, xmask,
1870               0, 0, 0, 0, w, h);
1871
1872       /* now create the 1-bit version */
1873       *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1874                           w, h, 1);
1875
1876       values.foreground = 1;
1877       values.background = 0;
1878
1879       /* Don't use XtGetGC, not read only */
1880       maskGC = XCreateGC(xDisplay, *mask,
1881                     GCForeground | GCBackground, &values);
1882       XCopyPlane(xDisplay, temp, *mask, maskGC,
1883                   0, 0, squareSize, squareSize, 0, 0, 1);
1884       XFreePixmap(xDisplay, temp);
1885     }
1886 }
1887
1888
1889 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1890
1891 void
1892 CreateXIMPieces ()
1893 {
1894     int piece, kind;
1895     char buf[MSG_SIZ];
1896     u_int ss;
1897     static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1898     XImage *ximtemp;
1899
1900     ss = squareSize;
1901
1902     /* The XSynchronize calls were copied from CreatePieces.
1903        Not sure if needed, but can't hurt */
1904     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1905                                      buffering bug */
1906
1907     /* temp needed by loadXIM() */
1908     ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1909                  0, 0, ss, ss, AllPlanes, XYPixmap);
1910
1911     if (strlen(appData.pixmapDirectory) == 0) {
1912       useImages = 0;
1913     } else {
1914         useImages = 1;
1915         if (appData.monoMode) {
1916           DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1917                             0, 2);
1918           ExitEvent(2);
1919         }
1920         fprintf(stderr, _("\nLoading XIMs...\n"));
1921         /* Load pieces */
1922         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1923             fprintf(stderr, "%d", piece+1);
1924             for (kind=0; kind<4; kind++) {
1925                 fprintf(stderr, ".");
1926                 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
1927                         ExpandPathName(appData.pixmapDirectory),
1928                         piece <= (int) WhiteKing ? "" : "w",
1929                         pieceBitmapNames[piece],
1930                         ximkind[kind], ss);
1931                 ximPieceBitmap[kind][piece] =
1932                   XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1933                             0, 0, ss, ss, AllPlanes, XYPixmap);
1934                 if (appData.debugMode)
1935                   fprintf(stderr, _("(File:%s:) "), buf);
1936                 loadXIM(ximPieceBitmap[kind][piece],
1937                         ximtemp, buf,
1938                         &(xpmPieceBitmap2[kind][piece]),
1939                         &(ximMaskPm2[piece]));
1940                 if(piece <= (int)WhiteKing)
1941                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
1942             }
1943             fprintf(stderr," ");
1944         }
1945         /* Load light and dark squares */
1946         /* If the LSQ and DSQ pieces don't exist, we will
1947            draw them with solid squares. */
1948         snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
1949         if (access(buf, 0) != 0) {
1950             useImageSqs = 0;
1951         } else {
1952             useImageSqs = 1;
1953             fprintf(stderr, _("light square "));
1954             ximLightSquare=
1955               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1956                         0, 0, ss, ss, AllPlanes, XYPixmap);
1957             if (appData.debugMode)
1958               fprintf(stderr, _("(File:%s:) "), buf);
1959
1960             loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
1961             fprintf(stderr, _("dark square "));
1962             snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
1963                     ExpandPathName(appData.pixmapDirectory), ss);
1964             if (appData.debugMode)
1965               fprintf(stderr, _("(File:%s:) "), buf);
1966             ximDarkSquare=
1967               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1968                         0, 0, ss, ss, AllPlanes, XYPixmap);
1969             loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
1970             xpmJailSquare = xpmLightSquare;
1971         }
1972         fprintf(stderr, _("Done.\n"));
1973     }
1974     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
1975 }
1976
1977 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
1978
1979 #if HAVE_LIBXPM
1980 void
1981 CreateXPMBoard (char *s, int kind)
1982 {
1983     XpmAttributes attr;
1984     attr.valuemask = 0;
1985     if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
1986     if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
1987         useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
1988     }
1989 }
1990
1991 void
1992 FreeXPMPieces ()
1993 {   // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
1994     // thisroutine has to be called t free the old piece pixmaps
1995     int piece, kind;
1996     for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
1997         for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
1998     if(useImageSqs) {
1999         XFreePixmap(xDisplay, xpmLightSquare);
2000         XFreePixmap(xDisplay, xpmDarkSquare);
2001     }
2002 }
2003
2004 void
2005 CreateXPMPieces ()
2006 {
2007     int piece, kind, r;
2008     char buf[MSG_SIZ];
2009     u_int ss = squareSize;
2010     XpmAttributes attr;
2011     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2012     XpmColorSymbol symbols[4];
2013     static int redo = False;
2014
2015     if(redo) FreeXPMPieces(); else redo = 1;
2016
2017     /* The XSynchronize calls were copied from CreatePieces.
2018        Not sure if needed, but can't hurt */
2019     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2020
2021     /* Setup translations so piece colors match square colors */
2022     symbols[0].name = "light_piece";
2023     symbols[0].value = appData.whitePieceColor;
2024     symbols[1].name = "dark_piece";
2025     symbols[1].value = appData.blackPieceColor;
2026     symbols[2].name = "light_square";
2027     symbols[2].value = appData.lightSquareColor;
2028     symbols[3].name = "dark_square";
2029     symbols[3].value = appData.darkSquareColor;
2030
2031     attr.valuemask = XpmColorSymbols;
2032     attr.colorsymbols = symbols;
2033     attr.numsymbols = 4;
2034
2035     if (appData.monoMode) {
2036       DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2037                         0, 2);
2038       ExitEvent(2);
2039     }
2040     if (strlen(appData.pixmapDirectory) == 0) {
2041         XpmPieces* pieces = builtInXpms;
2042         useImages = 1;
2043         /* Load pieces */
2044         while (pieces->size != squareSize && pieces->size) pieces++;
2045         if (!pieces->size) {
2046           fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2047           exit(1);
2048         }
2049         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2050             for (kind=0; kind<4; kind++) {
2051
2052                 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2053                                                pieces->xpm[piece][kind],
2054                                                &(xpmPieceBitmap2[kind][piece]),
2055                                                NULL, &attr)) != 0) {
2056                   fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2057                           r, buf);
2058                   exit(1);
2059                 }
2060                 if(piece <= (int) WhiteKing)
2061                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2062             }
2063         }
2064         useImageSqs = 0;
2065         xpmJailSquare = xpmLightSquare;
2066     } else {
2067         useImages = 1;
2068
2069         fprintf(stderr, _("\nLoading XPMs...\n"));
2070
2071         /* Load pieces */
2072         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2073             fprintf(stderr, "%d ", piece+1);
2074             for (kind=0; kind<4; kind++) {
2075               snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2076                         ExpandPathName(appData.pixmapDirectory),
2077                         piece > (int) WhiteKing ? "w" : "",
2078                         pieceBitmapNames[piece],
2079                         xpmkind[kind], ss);
2080                 if (appData.debugMode) {
2081                     fprintf(stderr, _("(File:%s:) "), buf);
2082                 }
2083                 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2084                                            &(xpmPieceBitmap2[kind][piece]),
2085                                            NULL, &attr)) != 0) {
2086                     if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2087                       // [HGM] missing: read of unorthodox piece failed; substitute King.
2088                       snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2089                                 ExpandPathName(appData.pixmapDirectory),
2090                                 xpmkind[kind], ss);
2091                         if (appData.debugMode) {
2092                             fprintf(stderr, _("(Replace by File:%s:) "), buf);
2093                         }
2094                         r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2095                                                 &(xpmPieceBitmap2[kind][piece]),
2096                                                 NULL, &attr);
2097                     }
2098                     if (r != 0) {
2099                         fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2100                                 r, buf);
2101                         exit(1);
2102                     }
2103                 }
2104                 if(piece <= (int) WhiteKing)
2105                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2106             }
2107         }
2108         /* Load light and dark squares */
2109         /* If the LSQ and DSQ pieces don't exist, we will
2110            draw them with solid squares. */
2111         fprintf(stderr, _("light square "));
2112         snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2113         if (access(buf, 0) != 0) {
2114             useImageSqs = 0;
2115         } else {
2116             useImageSqs = 1;
2117             if (appData.debugMode)
2118               fprintf(stderr, _("(File:%s:) "), buf);
2119
2120             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2121                                        &xpmLightSquare, NULL, &attr)) != 0) {
2122                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2123                 exit(1);
2124             }
2125             fprintf(stderr, _("dark square "));
2126             snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2127                     ExpandPathName(appData.pixmapDirectory), ss);
2128             if (appData.debugMode) {
2129                 fprintf(stderr, _("(File:%s:) "), buf);
2130             }
2131             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2132                                        &xpmDarkSquare, NULL, &attr)) != 0) {
2133                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2134                 exit(1);
2135             }
2136         }
2137         xpmJailSquare = xpmLightSquare;
2138         fprintf(stderr, _("Done.\n"));
2139     }
2140     oldVariant = -1; // kludge to force re-makig of animation masks
2141     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2142                                       buffering bug */
2143 }
2144 #endif /* HAVE_LIBXPM */
2145
2146 #if HAVE_LIBXPM
2147 /* No built-in bitmaps */
2148 void CreatePieces()
2149 {
2150     int piece, kind;
2151     char buf[MSG_SIZ];
2152     u_int ss = squareSize;
2153
2154     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2155                                      buffering bug */
2156
2157     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2158         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2159           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2160                    pieceBitmapNames[piece],
2161                    ss, kind == SOLID ? 's' : 'o');
2162           ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2163           if(piece <= (int)WhiteKing)
2164             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2165         }
2166     }
2167
2168     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2169                                       buffering bug */
2170 }
2171 #else
2172 /* With built-in bitmaps */
2173 void
2174 CreatePieces ()
2175 {
2176     BuiltInBits* bib = builtInBits;
2177     int piece, kind;
2178     char buf[MSG_SIZ];
2179     u_int ss = squareSize;
2180
2181     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2182                                      buffering bug */
2183
2184     while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2185
2186     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2187         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2188           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2189                    pieceBitmapNames[piece],
2190                    ss, kind == SOLID ? 's' : 'o');
2191           ReadBitmap(&pieceBitmap2[kind][piece], buf,
2192                      bib->bits[kind][piece], ss, ss);
2193           if(piece <= (int)WhiteKing)
2194             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2195         }
2196     }
2197
2198     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2199                                       buffering bug */
2200 }
2201 #endif
2202
2203 void
2204 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2205 {
2206     int x_hot, y_hot;
2207     u_int w, h;
2208     int errcode;
2209     char msg[MSG_SIZ], fullname[MSG_SIZ];
2210
2211     if (*appData.bitmapDirectory != NULLCHAR) {
2212       safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2213       strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2214       strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2215       errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2216                                 &w, &h, pm, &x_hot, &y_hot);
2217       fprintf(stderr, "load %s\n", name);
2218         if (errcode != BitmapSuccess) {
2219             switch (errcode) {
2220               case BitmapOpenFailed:
2221                 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2222                 break;
2223               case BitmapFileInvalid:
2224                 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2225                 break;
2226               case BitmapNoMemory:
2227                 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2228                         fullname);
2229                 break;
2230               default:
2231                 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2232                         errcode, fullname);
2233                 break;
2234             }
2235             fprintf(stderr, _("%s: %s...using built-in\n"),
2236                     programName, msg);
2237         } else if (w != wreq || h != hreq) {
2238             fprintf(stderr,
2239                     _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2240                     programName, fullname, w, h, wreq, hreq);
2241         } else {
2242             return;
2243         }
2244     }
2245     if (bits != NULL) {
2246         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2247                                     wreq, hreq);
2248     }
2249 }
2250
2251 void
2252 CreateGrid ()
2253 {
2254     int i, j;
2255
2256     if (lineGap == 0) return;
2257
2258     /* [HR] Split this into 2 loops for non-square boards. */
2259
2260     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2261         gridSegments[i].x1 = 0;
2262         gridSegments[i].x2 =
2263           lineGap + BOARD_WIDTH * (squareSize + lineGap);
2264         gridSegments[i].y1 = gridSegments[i].y2
2265           = lineGap / 2 + (i * (squareSize + lineGap));
2266     }
2267
2268     for (j = 0; j < BOARD_WIDTH + 1; j++) {
2269         gridSegments[j + i].y1 = 0;
2270         gridSegments[j + i].y2 =
2271           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2272         gridSegments[j + i].x1 = gridSegments[j + i].x2
2273           = lineGap / 2 + (j * (squareSize + lineGap));
2274     }
2275 }
2276
2277 void
2278 MarkMenuItem (char *menuRef, int state)
2279 {
2280     MenuItem *item = MenuNameToItem(menuRef);
2281
2282     if(item) {
2283         Arg args[2];
2284         XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2285         XtSetValues(item->handle, args, 1);
2286     }
2287 }
2288
2289 void
2290 EnableMenuItem (char *menuRef, int state)
2291 {
2292     MenuItem *item = MenuNameToItem(menuRef);
2293
2294     if(item) XtSetSensitive(item->handle, state);
2295 }
2296
2297 void
2298 EnableButtonBar (int state)
2299 {
2300     XtSetSensitive(optList[W_BUTTON].handle, state);
2301 }
2302
2303
2304 void
2305 SetMenuEnables (Enables *enab)
2306 {
2307   while (enab->name != NULL) {
2308     EnableMenuItem(enab->name, enab->value);
2309     enab++;
2310   }
2311 }
2312
2313 void
2314 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2315 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2316     int i;
2317     char *p;
2318     MenuItem *item;
2319     if(*nprms == 0) return;
2320     item = MenuNameToItem(prms[0]);
2321     if(item) ((MenuProc *) item->proc) ();
2322 }
2323
2324 static void
2325 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2326 {
2327     MenuProc *proc = (MenuProc *) addr;
2328
2329     (proc)();
2330 }
2331
2332 static void
2333 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2334 {
2335     RecentEngineEvent((int) (intptr_t) addr);
2336 }
2337
2338 void
2339 AppendMenuItem (char *msg, int n)
2340 {
2341     CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2342 }
2343
2344 void
2345 SetupDropMenu ()
2346 {
2347     int i, j, count;
2348     char label[32];
2349     Arg args[16];
2350     Widget entry;
2351     char* p;
2352
2353     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2354         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2355         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2356                    dmEnables[i].piece);
2357         XtSetSensitive(entry, p != NULL || !appData.testLegality
2358                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2359                                        && !appData.icsActive));
2360         count = 0;
2361         while (p && *p++ == dmEnables[i].piece) count++;
2362         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
2363         j = 0;
2364         XtSetArg(args[j], XtNlabel, label); j++;
2365         XtSetValues(entry, args, j);
2366     }
2367 }
2368
2369
2370 static void
2371 do_flash_delay (unsigned long msec)
2372 {
2373     TimeDelay(msec);
2374 }
2375
2376 void
2377 DrawBorder (int x, int y, int type)
2378 {
2379     GC gc = lineGC;
2380
2381     if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
2382
2383     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
2384                    squareSize+lineGap, squareSize+lineGap);
2385 }
2386
2387 static int
2388 CutOutSquare (int x, int y, int *x0, int *y0, int  kind)
2389 {
2390     int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2391     int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2392     *x0 = 0; *y0 = 0;
2393     if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2394     if(textureW[kind] < W*squareSize)
2395         *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2396     else
2397         *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2398     if(textureH[kind] < H*squareSize)
2399         *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2400     else
2401         *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2402     return 1;
2403 }
2404
2405 static void
2406 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2407 {   // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2408     int x0, y0;
2409     if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2410         XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2411                   squareSize, squareSize, x*fac, y*fac);
2412     } else
2413     if (useImages && useImageSqs) {
2414         Pixmap pm;
2415         switch (color) {
2416           case 1: /* light */
2417             pm = xpmLightSquare;
2418             break;
2419           case 0: /* dark */
2420             pm = xpmDarkSquare;
2421             break;
2422           case 2: /* neutral */
2423           default:
2424             pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2425             break;
2426         }
2427         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2428                   squareSize, squareSize, x*fac, y*fac);
2429     } else {
2430         GC gc;
2431         switch (color) {
2432           case 1: /* light */
2433             gc = lightSquareGC;
2434             break;
2435           case 0: /* dark */
2436             gc = darkSquareGC;
2437             break;
2438           case 2: /* neutral */
2439           default:
2440             gc = lineGC;
2441             break;
2442         }
2443         XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2444     }
2445 }
2446
2447 /*
2448    I split out the routines to draw a piece so that I could
2449    make a generic flash routine.
2450 */
2451 static void
2452 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2453 {
2454     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2455     switch (square_color) {
2456       case 1: /* light */
2457       case 2: /* neutral */
2458       default:
2459         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2460                   ? *pieceToOutline(piece)
2461                   : *pieceToSolid(piece),
2462                   dest, bwPieceGC, 0, 0,
2463                   squareSize, squareSize, x, y);
2464         break;
2465       case 0: /* dark */
2466         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2467                   ? *pieceToSolid(piece)
2468                   : *pieceToOutline(piece),
2469                   dest, wbPieceGC, 0, 0,
2470                   squareSize, squareSize, x, y);
2471         break;
2472     }
2473 }
2474
2475 static void
2476 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2477 {
2478     switch (square_color) {
2479       case 1: /* light */
2480       case 2: /* neutral */
2481       default:
2482         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2483                    ? *pieceToOutline(piece)
2484                    : *pieceToSolid(piece),
2485                    dest, bwPieceGC, 0, 0,
2486                    squareSize, squareSize, x, y, 1);
2487         break;
2488       case 0: /* dark */
2489         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2490                    ? *pieceToSolid(piece)
2491                    : *pieceToOutline(piece),
2492                    dest, wbPieceGC, 0, 0,
2493                    squareSize, squareSize, x, y, 1);
2494         break;
2495     }
2496 }
2497
2498 static void
2499 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2500 {
2501     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2502     switch (square_color) {
2503       case 1: /* light */
2504         XCopyPlane(xDisplay, *pieceToSolid(piece),
2505                    dest, (int) piece < (int) BlackPawn
2506                    ? wlPieceGC : blPieceGC, 0, 0,
2507                    squareSize, squareSize, x, y, 1);
2508         break;
2509       case 0: /* dark */
2510         XCopyPlane(xDisplay, *pieceToSolid(piece),
2511                    dest, (int) piece < (int) BlackPawn
2512                    ? wdPieceGC : bdPieceGC, 0, 0,
2513                    squareSize, squareSize, x, y, 1);
2514         break;
2515       case 2: /* neutral */
2516       default:
2517         break; // should never contain pieces
2518     }
2519 }
2520
2521 static void
2522 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2523 {
2524     int kind, p = piece;
2525
2526     switch (square_color) {
2527       case 1: /* light */
2528       case 2: /* neutral */
2529       default:
2530         if ((int)piece < (int) BlackPawn) {
2531             kind = 0;
2532         } else {
2533             kind = 2;
2534             piece -= BlackPawn;
2535         }
2536         break;
2537       case 0: /* dark */
2538         if ((int)piece < (int) BlackPawn) {
2539             kind = 1;
2540         } else {
2541             kind = 3;
2542             piece -= BlackPawn;
2543         }
2544         break;
2545     }
2546     if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2547     if(useTexture & square_color+1) {
2548         BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2549         XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2550         XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2551         XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2552         XSetClipMask(xDisplay, wlPieceGC, None);
2553         XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2554     } else
2555     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2556               dest, wlPieceGC, 0, 0,
2557               squareSize, squareSize, x, y);
2558 }
2559
2560 typedef void (*DrawFunc)();
2561
2562 DrawFunc
2563 ChooseDrawFunc ()
2564 {
2565     if (appData.monoMode) {
2566         if (DefaultDepth(xDisplay, xScreen) == 1) {
2567             return monoDrawPiece_1bit;
2568         } else {
2569             return monoDrawPiece;
2570         }
2571     } else {
2572         if (useImages)
2573           return colorDrawPieceImage;
2574         else
2575           return colorDrawPiece;
2576     }
2577 }
2578
2579 void
2580 DrawDot (int marker, int x, int y, int r)
2581 {
2582         if(appData.monoMode) {
2583             XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
2584                     x, y, r, r, 0, 64*360);
2585             XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
2586                     x, y, r, r, 0, 64*360);
2587         } else
2588         XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
2589                     x, y, r, r, 0, 64*360);
2590 }
2591
2592 void
2593 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2594 {   // basic front-end board-draw function: takes care of everything that can be in square:
2595     // piece, background, coordinate/count, marker dot
2596     int direction, font_ascent, font_descent;
2597     XCharStruct overall;
2598     DrawFunc drawfunc;
2599
2600     if (piece == EmptySquare) {
2601         BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2602     } else {
2603         drawfunc = ChooseDrawFunc();
2604         drawfunc(piece, square_color, x, y, xBoardWindow);
2605     }
2606
2607     if(align) { // square carries inscription (coord or piece count)
2608         int xx = x, yy = y;
2609         GC hGC = align < 3 ? coordGC : countGC;
2610         // first calculate where it goes
2611         XTextExtents(countFontStruct, string, 1, &direction,
2612                          &font_ascent, &font_descent, &overall);
2613         if (align == 1) {
2614             xx += squareSize - overall.width - 2;
2615             yy += squareSize - font_descent - 1;
2616         } else if (align == 2) {
2617             xx += 2, yy += font_ascent + 1;
2618         } else if (align == 3) {
2619             xx += squareSize - overall.width - 2;
2620             yy += font_ascent + 1;
2621         } else if (align == 4) {
2622             xx += 2, yy += font_ascent + 1;
2623         }
2624         // then draw it
2625         if (appData.monoMode) {
2626             XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2627         } else {
2628             XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2629         }
2630     }
2631
2632     if(marker) { // print fat marker dot, if requested
2633         DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2634     }
2635 }
2636
2637 void
2638 FlashDelay (int flash_delay)
2639 {
2640         XSync(xDisplay, False);
2641         if(flash_delay) do_flash_delay(flash_delay);
2642 }
2643
2644 double
2645 Fraction (int x, int start, int stop)
2646 {
2647    double f = ((double) x - start)/(stop - start);
2648    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2649    return f;
2650 }
2651
2652 static WindowPlacement wpNew;
2653
2654 void
2655 CoDrag (Widget sh, WindowPlacement *wp)
2656 {
2657     Arg args[16];
2658     int j=0, touch=0, fudge = 2;
2659     GetActualPlacement(sh, wp);
2660     if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x)         < fudge) touch = 1; else // right touch
2661     if(abs(wp->x + wp->width + 2*frameX - wpMain.x)            < fudge) touch = 2; else // left touch
2662     if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2663     if(abs(wp->y + wp->height + frameX + frameY - wpMain.y)    < fudge) touch = 4;      // top touch
2664     if(!touch ) return; // only windows that touch co-move
2665     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2666         int heightInc = wpNew.height - wpMain.height;
2667         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2668         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2669         wp->y += fracTop * heightInc;
2670         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2671         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2672     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2673         int widthInc = wpNew.width - wpMain.width;
2674         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2675         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2676         wp->y += fracLeft * widthInc;
2677         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2678         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2679     }
2680     wp->x += wpNew.x - wpMain.x;
2681     wp->y += wpNew.y - wpMain.y;
2682     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2683     if(touch == 3) wp->y += wpNew.height - wpMain.height;
2684     XtSetArg(args[j], XtNx, wp->x); j++;
2685     XtSetArg(args[j], XtNy, wp->y); j++;
2686     XtSetValues(sh, args, j);
2687 }
2688
2689 static XtIntervalId delayedDragID = 0;
2690
2691 void
2692 DragProc ()
2693 {
2694         GetActualPlacement(shellWidget, &wpNew);
2695         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2696            wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2697             return; // false alarm
2698         if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2699         if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2700         if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2701         if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2702         wpMain = wpNew;
2703         DrawPosition(True, NULL);
2704         delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2705 }
2706
2707
2708 void
2709 DelayedDrag ()
2710 {
2711     if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2712     delayedDragID =
2713       XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2714 }
2715
2716 void
2717 EventProc (Widget widget, caddr_t unused, XEvent *event)
2718 {
2719     if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2720         DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2721 }
2722
2723 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
2724 void
2725 DrawSeekAxis (int x, int y, int xTo, int yTo)
2726 {
2727       XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
2728 }
2729
2730 void
2731 DrawSeekBackground (int left, int top, int right, int bottom)
2732 {
2733     XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
2734 }
2735
2736 void
2737 DrawSeekText (char *buf, int x, int y)
2738 {
2739     XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
2740 }
2741
2742 void
2743 DrawSeekDot (int x, int y, int colorNr)
2744 {
2745     int square = colorNr & 0x80;
2746     GC color;
2747     colorNr &= 0x7F;
2748     color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
2749     if(square)
2750         XFillRectangle(xDisplay, xBoardWindow, color,
2751                 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2752     else
2753         XFillArc(xDisplay, xBoardWindow, color,
2754                 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
2755 }
2756
2757 void
2758 DrawGrid ()
2759 {
2760           XDrawSegments(xDisplay, xBoardWindow, lineGC,
2761                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
2762 }
2763
2764
2765 /*
2766  * event handler for redrawing the board
2767  */
2768 void
2769 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2770 {
2771     DrawPosition(True, NULL);
2772 }
2773
2774
2775 void
2776 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
2777 {   // [HGM] pv: walk PV
2778     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2779 }
2780
2781 static int savedIndex;  /* gross that this is global */
2782
2783 void
2784 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
2785 {
2786         String val;
2787         XawTextPosition index, dummy;
2788         Arg arg;
2789
2790         XawTextGetSelectionPos(w, &index, &dummy);
2791         XtSetArg(arg, XtNstring, &val);
2792         XtGetValues(w, &arg, 1);
2793         ReplaceComment(savedIndex, val);
2794         if(savedIndex != currentMove) ToNrEvent(savedIndex);
2795         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
2796 }
2797
2798 void
2799 EditCommentPopUp (int index, char *title, char *text)
2800 {
2801     savedIndex = index;
2802     if (text == NULL) text = "";
2803     NewCommentPopup(title, text, index);
2804 }
2805
2806 void
2807 CommentPopUp (char *title, char *text)
2808 {
2809     savedIndex = currentMove; // [HGM] vari
2810     NewCommentPopup(title, text, currentMove);
2811 }
2812
2813 void
2814 CommentPopDown ()
2815 {
2816     PopDown(CommentDlg);
2817 }
2818
2819 static char *openName;
2820 FILE *openFP;
2821
2822 void
2823 DelayedLoad ()
2824 {
2825   (void) (*fileProc)(openFP, 0, openName);
2826 }
2827
2828 void
2829 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
2830 {
2831     fileProc = proc;            /* I can't see a way not */
2832     fileOpenMode = openMode;    /*   to use globals here */
2833     {   // [HGM] use file-selector dialog stolen from Ghostview
2834         int index; // this is not supported yet
2835         Browse(BoardWindow, label, (def[0] ? def : NULL), filter, False, openMode, &openName, &openFP);
2836     }
2837 }
2838
2839
2840 /* Disable all user input other than deleting the window */
2841 static int frozen = 0;
2842
2843 void
2844 FreezeUI ()
2845 {
2846   if (frozen) return;
2847   /* Grab by a widget that doesn't accept input */
2848   XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
2849   frozen = 1;
2850 }
2851
2852 /* Undo a FreezeUI */
2853 void
2854 ThawUI ()
2855 {
2856   if (!frozen) return;
2857   XtRemoveGrab(optList[W_MESSG].handle);
2858   frozen = 0;
2859 }
2860
2861 void
2862 ModeHighlight ()
2863 {
2864     Arg args[16];
2865     static int oldPausing = FALSE;
2866     static GameMode oldmode = (GameMode) -1;
2867     char *wname;
2868
2869     if (!boardWidget || !XtIsRealized(boardWidget)) return;
2870
2871     if (pausing != oldPausing) {
2872         oldPausing = pausing;
2873         MarkMenuItem("Mode.Pause", pausing);
2874
2875         if (appData.showButtonBar) {
2876           /* Always toggle, don't set.  Previous code messes up when
2877              invoked while the button is pressed, as releasing it
2878              toggles the state again. */
2879           {
2880             Pixel oldbg, oldfg;
2881             XtSetArg(args[0], XtNbackground, &oldbg);
2882             XtSetArg(args[1], XtNforeground, &oldfg);
2883             XtGetValues(optList[W_PAUSE].handle,
2884                         args, 2);
2885             XtSetArg(args[0], XtNbackground, oldfg);
2886             XtSetArg(args[1], XtNforeground, oldbg);
2887           }
2888           XtSetValues(optList[W_PAUSE].handle, args, 2);
2889         }
2890     }
2891
2892     wname = ModeToWidgetName(oldmode);
2893     if (wname != NULL) {
2894         MarkMenuItem(wname, False);
2895     }
2896     wname = ModeToWidgetName(gameMode);
2897     if (wname != NULL) {
2898         MarkMenuItem(wname, True);
2899     }
2900     oldmode = gameMode;
2901     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
2902
2903     /* Maybe all the enables should be handled here, not just this one */
2904     EnableMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
2905 }
2906
2907
2908 /*
2909  * Button/menu procedures
2910  */
2911
2912 /* this variable is shared between CopyPositionProc and SendPositionSelection */
2913 char *selected_fen_position=NULL;
2914
2915 Boolean
2916 SendPositionSelection (Widget w, Atom *selection, Atom *target,
2917                        Atom *type_return, XtPointer *value_return,
2918                        unsigned long *length_return, int *format_return)
2919 {
2920   char *selection_tmp;
2921
2922 //  if (!selected_fen_position) return False; /* should never happen */
2923   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
2924    if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
2925     FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
2926     long len;
2927     size_t count;
2928     if (f == NULL) return False;
2929     fseek(f, 0, 2);
2930     len = ftell(f);
2931     rewind(f);
2932     selection_tmp = XtMalloc(len + 1);
2933     count = fread(selection_tmp, 1, len, f);
2934     fclose(f);
2935     if (len != count) {
2936       XtFree(selection_tmp);
2937       return False;
2938     }
2939     selection_tmp[len] = NULLCHAR;
2940    } else {
2941     /* note: since no XtSelectionDoneProc was registered, Xt will
2942      * automatically call XtFree on the value returned.  So have to
2943      * make a copy of it allocated with XtMalloc */
2944     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
2945     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
2946    }
2947
2948     *value_return=selection_tmp;
2949     *length_return=strlen(selection_tmp);
2950     *type_return=*target;
2951     *format_return = 8; /* bits per byte */
2952     return True;
2953   } else if (*target == XA_TARGETS(xDisplay)) {
2954     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
2955     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
2956     targets_tmp[1] = XA_STRING;
2957     *value_return = targets_tmp;
2958     *type_return = XA_ATOM;
2959     *length_return = 2;
2960 #if 0
2961     // This code leads to a read of value_return out of bounds on 64-bit systems.
2962     // Other code which I have seen always sets *format_return to 32 independent of
2963     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
2964     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
2965     *format_return = 8 * sizeof(Atom);
2966     if (*format_return > 32) {
2967       *length_return *= *format_return / 32;
2968       *format_return = 32;
2969     }
2970 #else
2971     *format_return = 32;
2972 #endif
2973     return True;
2974   } else {
2975     return False;
2976   }
2977 }
2978
2979 /* note: when called from menu all parameters are NULL, so no clue what the
2980  * Widget which was clicked on was, or what the click event was
2981  */
2982 void
2983 CopySomething (char *src)
2984 {
2985     selected_fen_position = src;
2986     /*
2987      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2988      * have a notion of a position that is selected but not copied.
2989      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2990      */
2991     XtOwnSelection(menuBarWidget, XA_PRIMARY,
2992                    CurrentTime,
2993                    SendPositionSelection,
2994                    NULL/* lose_ownership_proc */ ,
2995                    NULL/* transfer_done_proc */);
2996     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2997                    CurrentTime,
2998                    SendPositionSelection,
2999                    NULL/* lose_ownership_proc */ ,
3000                    NULL/* transfer_done_proc */);
3001 }
3002
3003 /* function called when the data to Paste is ready */
3004 static void
3005 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3006                  Atom *type, XtPointer value, unsigned long *len, int *format)
3007 {
3008   char *fenstr=value;
3009   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3010   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3011   EditPositionPasteFEN(fenstr);
3012   XtFree(value);
3013 }
3014
3015 /* called when Paste Position button is pressed,
3016  * all parameters will be NULL */
3017 void
3018 PastePositionProc ()
3019 {
3020     XtGetSelectionValue(menuBarWidget,
3021       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3022       /* (XtSelectionCallbackProc) */ PastePositionCB,
3023       NULL, /* client_data passed to PastePositionCB */
3024
3025       /* better to use the time field from the event that triggered the
3026        * call to this function, but that isn't trivial to get
3027        */
3028       CurrentTime
3029     );
3030     return;
3031 }
3032
3033 /* note: when called from menu all parameters are NULL, so no clue what the
3034  * Widget which was clicked on was, or what the click event was
3035  */
3036 /* function called when the data to Paste is ready */
3037 static void
3038 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3039              Atom *type, XtPointer value, unsigned long *len, int *format)
3040 {
3041   FILE* f;
3042   if (value == NULL || *len == 0) {
3043     return; /* nothing had been selected to copy */
3044   }
3045   f = fopen(gamePasteFilename, "w");
3046   if (f == NULL) {
3047     DisplayError(_("Can't open temp file"), errno);
3048     return;
3049   }
3050   fwrite(value, 1, *len, f);
3051   fclose(f);
3052   XtFree(value);
3053   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3054 }
3055
3056 /* called when Paste Game button is pressed,
3057  * all parameters will be NULL */
3058 void
3059 PasteGameProc ()
3060 {
3061     XtGetSelectionValue(menuBarWidget,
3062       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3063       /* (XtSelectionCallbackProc) */ PasteGameCB,
3064       NULL, /* client_data passed to PasteGameCB */
3065
3066       /* better to use the time field from the event that triggered the
3067        * call to this function, but that isn't trivial to get
3068        */
3069       CurrentTime
3070     );
3071     return;
3072 }
3073
3074
3075 void
3076 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3077 {
3078     QuitProc();
3079 }
3080
3081 int
3082 ShiftKeys ()
3083 {   // bassic primitive for determining if modifier keys are pressed
3084     long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3085     char keys[32];
3086     int i,j,  k=0;
3087     XQueryKeymap(xDisplay,keys);
3088     for(i=0; i<6; i++) {
3089         k <<= 1;
3090         j = XKeysymToKeycode(xDisplay, codes[i]);
3091         k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3092     }
3093     return k;
3094 }
3095
3096 static void
3097 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3098 {
3099     char buf[10];
3100     KeySym sym;
3101     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3102     if ( n == 1 && *buf >= 32 // printable
3103          && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3104         ) BoxAutoPopUp (buf);
3105 }
3106
3107 static void
3108 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3109 {   // [HGM] input: let up-arrow recall previous line from history
3110     IcsKey(1);
3111 }
3112
3113 static void
3114 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3115 {   // [HGM] input: let down-arrow recall next line from history
3116     IcsKey(-1);
3117 }
3118
3119 static void
3120 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3121 {
3122     IcsKey(0);
3123 }
3124
3125 void
3126 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3127 {
3128         if (!TempBackwardActive) {
3129                 TempBackwardActive = True;
3130                 BackwardEvent();
3131         }
3132 }
3133
3134 void
3135 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3136 {
3137         /* Check to see if triggered by a key release event for a repeating key.
3138          * If so the next queued event will be a key press of the same key at the same time */
3139         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3140                 XEvent next;
3141                 XPeekEvent(xDisplay, &next);
3142                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3143                         next.xkey.keycode == event->xkey.keycode)
3144                                 return;
3145         }
3146     ForwardEvent();
3147         TempBackwardActive = False;
3148 }
3149
3150 void
3151 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3152 {   // called as key binding
3153     char buf[MSG_SIZ];
3154     String name;
3155     if (nprms && *nprms > 0)
3156       name = prms[0];
3157     else
3158       name = "xboard";
3159     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3160     system(buf);
3161 }
3162
3163 void
3164 SetWindowTitle (char *text, char *title, char *icon)
3165 {
3166     Arg args[16];
3167     int i;
3168     if (appData.titleInWindow) {
3169         i = 0;
3170         XtSetArg(args[i], XtNlabel, text);   i++;
3171         XtSetValues(titleWidget, args, i);
3172     }
3173     i = 0;
3174     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
3175     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
3176     XtSetValues(shellWidget, args, i);
3177     XSync(xDisplay, False);
3178 }
3179
3180
3181 static int
3182 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3183 {
3184     return 0;
3185 }
3186
3187 void
3188 DisplayIcsInteractionTitle (String message)
3189 {
3190   if (oldICSInteractionTitle == NULL) {
3191     /* Magic to find the old window title, adapted from vim */
3192     char *wina = getenv("WINDOWID");
3193     if (wina != NULL) {
3194       Window win = (Window) atoi(wina);
3195       Window root, parent, *children;
3196       unsigned int nchildren;
3197       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3198       for (;;) {
3199         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3200         if (!XQueryTree(xDisplay, win, &root, &parent,
3201                         &children, &nchildren)) break;
3202         if (children) XFree((void *)children);
3203         if (parent == root || parent == 0) break;
3204         win = parent;
3205       }
3206       XSetErrorHandler(oldHandler);
3207     }
3208     if (oldICSInteractionTitle == NULL) {
3209       oldICSInteractionTitle = "xterm";
3210     }
3211   }
3212   printf("\033]0;%s\007", message);
3213   fflush(stdout);
3214 }
3215
3216
3217 XtIntervalId delayedEventTimerXID = 0;
3218 DelayedEventCallback delayedEventCallback = 0;
3219
3220 void
3221 FireDelayedEvent ()
3222 {
3223     delayedEventTimerXID = 0;
3224     delayedEventCallback();
3225 }
3226
3227 void
3228 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3229 {
3230     if(delayedEventTimerXID && delayedEventCallback == cb)
3231         // [HGM] alive: replace, rather than add or flush identical event
3232         XtRemoveTimeOut(delayedEventTimerXID);
3233     delayedEventCallback = cb;
3234     delayedEventTimerXID =
3235       XtAppAddTimeOut(appContext, millisec,
3236                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3237 }
3238
3239 DelayedEventCallback
3240 GetDelayedEvent ()
3241 {
3242   if (delayedEventTimerXID) {
3243     return delayedEventCallback;
3244   } else {
3245     return NULL;
3246   }
3247 }
3248
3249 void
3250 CancelDelayedEvent ()
3251 {
3252   if (delayedEventTimerXID) {
3253     XtRemoveTimeOut(delayedEventTimerXID);
3254     delayedEventTimerXID = 0;
3255   }
3256 }
3257
3258 XtIntervalId loadGameTimerXID = 0;
3259
3260 int
3261 LoadGameTimerRunning ()
3262 {
3263     return loadGameTimerXID != 0;
3264 }
3265
3266 int
3267 StopLoadGameTimer ()
3268 {
3269     if (loadGameTimerXID != 0) {
3270         XtRemoveTimeOut(loadGameTimerXID);
3271         loadGameTimerXID = 0;
3272         return TRUE;
3273     } else {
3274         return FALSE;
3275     }
3276 }
3277
3278 void
3279 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3280 {
3281     loadGameTimerXID = 0;
3282     AutoPlayGameLoop();
3283 }
3284
3285 void
3286 StartLoadGameTimer (long millisec)
3287 {
3288     loadGameTimerXID =
3289       XtAppAddTimeOut(appContext, millisec,
3290                       (XtTimerCallbackProc) LoadGameTimerCallback,
3291                       (XtPointer) 0);
3292 }
3293
3294 XtIntervalId analysisClockXID = 0;
3295
3296 void
3297 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3298 {
3299     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3300          || appData.icsEngineAnalyze) { // [DM]
3301         AnalysisPeriodicEvent(0);
3302         StartAnalysisClock();
3303     }
3304 }
3305
3306 void
3307 StartAnalysisClock ()
3308 {
3309     analysisClockXID =
3310       XtAppAddTimeOut(appContext, 2000,
3311                       (XtTimerCallbackProc) AnalysisClockCallback,
3312                       (XtPointer) 0);
3313 }
3314
3315 XtIntervalId clockTimerXID = 0;
3316
3317 int
3318 ClockTimerRunning ()
3319 {
3320     return clockTimerXID != 0;
3321 }
3322
3323 int
3324 StopClockTimer ()
3325 {
3326     if (clockTimerXID != 0) {
3327         XtRemoveTimeOut(clockTimerXID);
3328         clockTimerXID = 0;
3329         return TRUE;
3330     } else {
3331         return FALSE;
3332     }
3333 }
3334
3335 void
3336 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3337 {
3338     clockTimerXID = 0;
3339     DecrementClocks();
3340 }
3341
3342 void
3343 StartClockTimer (long millisec)
3344 {
3345     clockTimerXID =
3346       XtAppAddTimeOut(appContext, millisec,
3347                       (XtTimerCallbackProc) ClockTimerCallback,
3348                       (XtPointer) 0);
3349 }
3350
3351 void
3352 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3353 {
3354     char buf[MSG_SIZ];
3355     Arg args[16];
3356     Widget w = (Widget) opt->handle;
3357
3358     /* check for low time warning */
3359     Pixel foregroundOrWarningColor = timerForegroundPixel;
3360
3361     if (timer > 0 &&
3362         appData.lowTimeWarning &&
3363         (timer / 1000) < appData.icsAlarmTime)
3364       foregroundOrWarningColor = lowTimeWarningColor;
3365
3366     if (appData.clockMode) {
3367       snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
3368       XtSetArg(args[0], XtNlabel, buf);
3369     } else {
3370       snprintf(buf, MSG_SIZ, "%s  ", color);
3371       XtSetArg(args[0], XtNlabel, buf);
3372     }
3373
3374     if (highlight) {
3375
3376         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3377         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3378     } else {
3379         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3380         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3381     }
3382
3383     XtSetValues(w, args, 3);
3384 }
3385
3386 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3387
3388 void
3389 SetClockIcon (int color)
3390 {
3391     Arg args[16];
3392     Pixmap pm = *clockIcons[color];
3393     if (iconPixmap != pm) {
3394         iconPixmap = pm;
3395         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3396         XtSetValues(shellWidget, args, 1);
3397     }
3398 }
3399
3400 void
3401 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3402 {
3403     InputSource *is = (InputSource *) closure;
3404     int count;
3405     int error;
3406     char *p, *q;
3407
3408     if (is->lineByLine) {
3409         count = read(is->fd, is->unused,
3410                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3411         if (count <= 0) {
3412             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3413             return;
3414         }
3415         is->unused += count;
3416         p = is->buf;
3417         while (p < is->unused) {
3418             q = memchr(p, '\n', is->unused - p);
3419             if (q == NULL) break;
3420             q++;
3421             (is->func)(is, is->closure, p, q - p, 0);
3422             p = q;
3423         }
3424         q = is->buf;
3425         while (p < is->unused) {
3426             *q++ = *p++;
3427         }
3428         is->unused = q;
3429     } else {
3430         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3431         if (count == -1)
3432           error = errno;
3433         else
3434           error = 0;
3435         (is->func)(is, is->closure, is->buf, count, error);
3436     }
3437 }
3438
3439 InputSourceRef
3440 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3441 {
3442     InputSource *is;
3443     ChildProc *cp = (ChildProc *) pr;
3444
3445     is = (InputSource *) calloc(1, sizeof(InputSource));
3446     is->lineByLine = lineByLine;
3447     is->func = func;
3448     if (pr == NoProc) {
3449         is->kind = CPReal;
3450         is->fd = fileno(stdin);
3451     } else {
3452         is->kind = cp->kind;
3453         is->fd = cp->fdFrom;
3454     }
3455     if (lineByLine) {
3456         is->unused = is->buf;
3457     }
3458
3459     is->xid = XtAppAddInput(appContext, is->fd,
3460                             (XtPointer) (XtInputReadMask),
3461                             (XtInputCallbackProc) DoInputCallback,
3462                             (XtPointer) is);
3463     is->closure = closure;
3464     return (InputSourceRef) is;
3465 }
3466
3467 void
3468 RemoveInputSource (InputSourceRef isr)
3469 {
3470     InputSource *is = (InputSource *) isr;
3471
3472     if (is->xid == 0) return;
3473     XtRemoveInput(is->xid);
3474     is->xid = 0;
3475 }
3476
3477 /****   Animation code by Hugh Fisher, DCS, ANU. ****/
3478
3479 /*      Masks for XPM pieces. Black and white pieces can have
3480         different shapes, but in the interest of retaining my
3481         sanity pieces must have the same outline on both light
3482         and dark squares, and all pieces must use the same
3483         background square colors/images.                */
3484
3485 static int xpmDone = 0;
3486 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3487 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3488
3489 static void
3490 CreateAnimMasks (int pieceDepth)
3491 {
3492   ChessSquare   piece;
3493   Pixmap        buf;
3494   GC            bufGC, maskGC;
3495   int           kind, n;
3496   unsigned long plane;
3497   XGCValues     values;
3498
3499   /* Need a bitmap just to get a GC with right depth */
3500   buf = XCreatePixmap(xDisplay, xBoardWindow,
3501                         8, 8, 1);
3502   values.foreground = 1;
3503   values.background = 0;
3504   /* Don't use XtGetGC, not read only */
3505   maskGC = XCreateGC(xDisplay, buf,
3506                     GCForeground | GCBackground, &values);
3507   XFreePixmap(xDisplay, buf);
3508
3509   buf = XCreatePixmap(xDisplay, xBoardWindow,
3510                       squareSize, squareSize, pieceDepth);
3511   values.foreground = XBlackPixel(xDisplay, xScreen);
3512   values.background = XWhitePixel(xDisplay, xScreen);
3513   bufGC = XCreateGC(xDisplay, buf,
3514                     GCForeground | GCBackground, &values);
3515
3516   for (piece = WhitePawn; piece <= BlackKing; piece++) {
3517     /* Begin with empty mask */
3518     if(!xpmDone) // [HGM] pieces: keep using existing
3519     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3520                                  squareSize, squareSize, 1);
3521     XSetFunction(xDisplay, maskGC, GXclear);
3522     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3523                    0, 0, squareSize, squareSize);
3524
3525     /* Take a copy of the piece */
3526     if (White(piece))
3527       kind = 0;
3528     else
3529       kind = 2;
3530     XSetFunction(xDisplay, bufGC, GXcopy);
3531     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3532               buf, bufGC,
3533               0, 0, squareSize, squareSize, 0, 0);
3534
3535     /* XOR the background (light) over the piece */
3536     XSetFunction(xDisplay, bufGC, GXxor);
3537     if (useImageSqs)
3538       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3539                 0, 0, squareSize, squareSize, 0, 0);
3540     else {
3541       XSetForeground(xDisplay, bufGC, lightSquareColor);
3542       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3543     }
3544
3545     /* We now have an inverted piece image with the background
3546        erased. Construct mask by just selecting all the non-zero
3547        pixels - no need to reconstruct the original image.      */
3548     XSetFunction(xDisplay, maskGC, GXor);
3549     plane = 1;
3550     /* Might be quicker to download an XImage and create bitmap
3551        data from it rather than this N copies per piece, but it
3552        only takes a fraction of a second and there is a much
3553        longer delay for loading the pieces.             */
3554     for (n = 0; n < pieceDepth; n ++) {
3555       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3556                  0, 0, squareSize, squareSize,
3557                  0, 0, plane);
3558       plane = plane << 1;
3559     }
3560   }
3561   /* Clean up */
3562   XFreePixmap(xDisplay, buf);
3563   XFreeGC(xDisplay, bufGC);
3564   XFreeGC(xDisplay, maskGC);
3565 }
3566
3567 static void
3568 InitAnimState (AnimNr anr, XWindowAttributes *info)
3569 {
3570   XtGCMask  mask;
3571   XGCValues values;
3572
3573   /* Each buffer is square size, same depth as window */
3574   animBufs[anr+4] = xBoardWindow;
3575   animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3576                         squareSize, squareSize, info->depth);
3577   animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3578                         squareSize, squareSize, info->depth);
3579
3580   /* Create a plain GC for blitting */
3581   mask = GCForeground | GCBackground | GCFunction |
3582          GCPlaneMask | GCGraphicsExposures;
3583   values.foreground = XBlackPixel(xDisplay, xScreen);
3584   values.background = XWhitePixel(xDisplay, xScreen);
3585   values.function   = GXcopy;
3586   values.plane_mask = AllPlanes;
3587   values.graphics_exposures = False;
3588   animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3589
3590   /* Piece will be copied from an existing context at
3591      the start of each new animation/drag. */
3592   animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3593
3594   /* Outline will be a read-only copy of an existing */
3595   animGCs[anr+4] = None;
3596 }
3597
3598 void
3599 CreateAnimVars ()
3600 {
3601   XWindowAttributes info;
3602
3603   if (xpmDone && gameInfo.variant == oldVariant) return;
3604   if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3605   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3606
3607   InitAnimState(Game, &info);
3608   InitAnimState(Player, &info);
3609
3610   /* For XPM pieces, we need bitmaps to use as masks. */
3611   if (useImages)
3612     CreateAnimMasks(info.depth), xpmDone = 1;
3613 }
3614
3615 #ifndef HAVE_USLEEP
3616
3617 static Boolean frameWaiting;
3618
3619 static RETSIGTYPE
3620 FrameAlarm (int sig)
3621 {
3622   frameWaiting = False;
3623   /* In case System-V style signals.  Needed?? */
3624   signal(SIGALRM, FrameAlarm);
3625 }
3626
3627 void
3628 FrameDelay (int time)
3629 {
3630   struct itimerval delay;
3631
3632   XSync(xDisplay, False);
3633
3634   if (time > 0) {
3635     frameWaiting = True;
3636     signal(SIGALRM, FrameAlarm);
3637     delay.it_interval.tv_sec =
3638       delay.it_value.tv_sec = time / 1000;
3639     delay.it_interval.tv_usec =
3640       delay.it_value.tv_usec = (time % 1000) * 1000;
3641     setitimer(ITIMER_REAL, &delay, NULL);
3642     while (frameWaiting) pause();
3643     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3644     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3645     setitimer(ITIMER_REAL, &delay, NULL);
3646   }
3647 }
3648
3649 #else
3650
3651 void
3652 FrameDelay (int time)
3653 {
3654   XSync(xDisplay, False);
3655   if (time > 0)
3656     usleep(time * 1000);
3657 }
3658
3659 #endif
3660
3661 static void
3662 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3663 {
3664   GC source;
3665
3666   /* Bitmap for piece being moved. */
3667   if (appData.monoMode) {
3668       *mask = *pieceToSolid(piece);
3669   } else if (useImages) {
3670 #if HAVE_LIBXPM
3671       *mask = xpmMask[piece];
3672 #else
3673       *mask = ximMaskPm[piece];
3674 #endif
3675   } else {
3676       *mask = *pieceToSolid(piece);
3677   }
3678
3679   /* GC for piece being moved. Square color doesn't matter, but
3680      since it gets modified we make a copy of the original. */
3681   if (White(piece)) {
3682     if (appData.monoMode)
3683       source = bwPieceGC;
3684     else
3685       source = wlPieceGC;
3686   } else {
3687     if (appData.monoMode)
3688       source = wbPieceGC;
3689     else
3690       source = blPieceGC;
3691   }
3692   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3693
3694   /* Outline only used in mono mode and is not modified */
3695   if (White(piece))
3696     *outline = bwPieceGC;
3697   else
3698     *outline = wbPieceGC;
3699 }
3700
3701 static void
3702 OverlayPiece (ChessSquare piece, GC clip, GC outline,  Drawable dest)
3703 {
3704   int   kind;
3705
3706   if (!useImages) {
3707     /* Draw solid rectangle which will be clipped to shape of piece */
3708     XFillRectangle(xDisplay, dest, clip,
3709                    0, 0, squareSize, squareSize);
3710     if (appData.monoMode)
3711       /* Also draw outline in contrasting color for black
3712          on black / white on white cases                */
3713       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3714                  0, 0, squareSize, squareSize, 0, 0, 1);
3715   } else {
3716     /* Copy the piece */
3717     if (White(piece))
3718       kind = 0;
3719     else
3720       kind = 2;
3721     if(appData.upsideDown && flipView) kind ^= 2;
3722     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3723               dest, clip,
3724               0, 0, squareSize, squareSize,
3725               0, 0);
3726   }
3727 }
3728
3729 void
3730 InsertPiece (AnimNr anr, ChessSquare piece)
3731 {
3732   OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
3733 }
3734
3735 void
3736 DrawBlank (AnimNr anr, int x, int y, int startColor)
3737 {
3738     BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
3739 }
3740
3741 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
3742                  int srcX, int srcY, int width, int height, int destX, int destY)
3743 {
3744     XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
3745                 srcX, srcY, width, height, destX, destY);
3746 }
3747
3748 void
3749 SetDragPiece (AnimNr anr, ChessSquare piece)
3750 {
3751   Pixmap mask;
3752   /* The piece will be drawn using its own bitmap as a matte    */
3753   SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
3754   XSetClipMask(xDisplay, animGCs[anr+2], mask);
3755 }
3756
3757 /* [AS] Arrow highlighting support */
3758
3759 void
3760 DrawPolygon (Pnt arrow[], int nr)
3761 {
3762     XPoint pts[10];
3763     int i;
3764     for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
3765     XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
3766     if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
3767 }
3768
3769 void
3770 UpdateLogos (int displ)
3771 {
3772     return; // no logos in XBoard yet
3773 }
3774