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