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