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