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