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