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