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