Redo coordinate / piece-count printing ith cairo
[xboard.git] / xboard.c
1 /*
2  * xboard.c -- X front end for XBoard
3  *
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,
5  * Massachusetts.
6  *
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8  * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
9  *
10  * The following terms apply to Digital Equipment Corporation's copyright
11  * interest in XBoard:
12  * ------------------------------------------------------------------------
13  * All Rights Reserved
14  *
15  * Permission to use, copy, modify, and distribute this software and its
16  * documentation for any purpose and without fee is hereby granted,
17  * provided that the above copyright notice appear in all copies and that
18  * both that copyright notice and this permission notice appear in
19  * supporting documentation, and that the name of Digital not be
20  * used in advertising or publicity pertaining to distribution of the
21  * software without specific, written prior permission.
22  *
23  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29  * SOFTWARE.
30  * ------------------------------------------------------------------------
31  *
32  * The following terms apply to the enhanced version of XBoard
33  * distributed by the Free Software Foundation:
34  * ------------------------------------------------------------------------
35  *
36  * GNU XBoard is free software: you can redistribute it and/or modify
37  * it under the terms of the GNU General Public License as published by
38  * the Free Software Foundation, either version 3 of the License, or (at
39  * your option) any later version.
40  *
41  * GNU XBoard is distributed in the hope that it will be useful, but
42  * WITHOUT ANY WARRANTY; without even the implied warranty of
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44  * General Public License for more details.
45  *
46  * You should have received a copy of the GNU General Public License
47  * along with this program. If not, see http://www.gnu.org/licenses/.  *
48  *
49  *------------------------------------------------------------------------
50  ** See the file ChangeLog for a revision history.  */
51
52 #define HIGHDRAG 1
53
54 #include "config.h"
55
56 #include <stdio.h>
57 #include <ctype.h>
58 #include <signal.h>
59 #include <errno.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <pwd.h>
63 #include <math.h>
64 #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             if(*appData.pngDirectory) {
2862                 cairo_t *cr = cairo_create (csBoardWindow);
2863                 cairo_select_font_face (cr, "Sans",
2864                             CAIRO_FONT_SLANT_NORMAL,
2865                             CAIRO_FONT_WEIGHT_BOLD);
2866
2867                 cairo_set_font_size (cr, squareSize/4);
2868
2869                 cairo_move_to (cr, xx-1, yy);
2870                 if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2871                 else          cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
2872                 cairo_show_text (cr, string);
2873
2874                 /* free memory */
2875                 cairo_destroy (cr);
2876             } else
2877             XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2878         }
2879     }
2880
2881     if(marker) { // print fat marker dot, if requested
2882         DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2883     }
2884 }
2885
2886 void
2887 FlashDelay (int flash_delay)
2888 {
2889         XSync(xDisplay, False);
2890         if(flash_delay) do_flash_delay(flash_delay);
2891 }
2892
2893 double
2894 Fraction (int x, int start, int stop)
2895 {
2896    double f = ((double) x - start)/(stop - start);
2897    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2898    return f;
2899 }
2900
2901 static WindowPlacement wpNew;
2902
2903 void
2904 CoDrag (Widget sh, WindowPlacement *wp)
2905 {
2906     Arg args[16];
2907     int j=0, touch=0, fudge = 2;
2908     GetActualPlacement(sh, wp);
2909     if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x)         < fudge) touch = 1; else // right touch
2910     if(abs(wp->x + wp->width + 2*frameX - wpMain.x)            < fudge) touch = 2; else // left touch
2911     if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2912     if(abs(wp->y + wp->height + frameX + frameY - wpMain.y)    < fudge) touch = 4;      // top touch
2913     if(!touch ) return; // only windows that touch co-move
2914     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2915         int heightInc = wpNew.height - wpMain.height;
2916         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2917         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2918         wp->y += fracTop * heightInc;
2919         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2920         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2921     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2922         int widthInc = wpNew.width - wpMain.width;
2923         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2924         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2925         wp->y += fracLeft * widthInc;
2926         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2927         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2928     }
2929     wp->x += wpNew.x - wpMain.x;
2930     wp->y += wpNew.y - wpMain.y;
2931     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2932     if(touch == 3) wp->y += wpNew.height - wpMain.height;
2933     XtSetArg(args[j], XtNx, wp->x); j++;
2934     XtSetArg(args[j], XtNy, wp->y); j++;
2935     XtSetValues(sh, args, j);
2936 }
2937
2938 void
2939 ReSize (WindowPlacement *wp)
2940 {
2941         int sqx, sqy;
2942         if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
2943         sqx = (wp->width  - lineGap - marginW) / BOARD_WIDTH - lineGap;
2944         sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
2945         if(sqy < sqx) sqx = sqy;
2946         if(sqx != squareSize) {
2947             squareSize = sqx; // adopt new square size
2948             NewSurfaces();
2949             CreatePNGPieces(); // make newly scaled pieces
2950             InitDrawingSizes(0, 0); // creates grid etc.
2951         }
2952 }
2953
2954 static XtIntervalId delayedDragID = 0;
2955
2956 void
2957 DragProc ()
2958 {
2959         static int busy;
2960         if(busy) return;
2961
2962         busy = 1;
2963         GetActualPlacement(shellWidget, &wpNew);
2964         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2965            wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
2966             busy = 0; return; // false alarm
2967         }
2968         ReSize(&wpNew);
2969         if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2970         if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2971         if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2972         if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2973         wpMain = wpNew;
2974         DrawPosition(True, NULL);
2975         delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2976         busy = 0;
2977 }
2978
2979
2980 void
2981 DelayedDrag ()
2982 {
2983     if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2984     delayedDragID =
2985       XtAppAddTimeOut(appContext, 100, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2986 }
2987
2988 void
2989 EventProc (Widget widget, caddr_t unused, XEvent *event)
2990 {
2991     if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2992         DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2993 }
2994
2995 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
2996
2997 float
2998 Color (char *col, int n)
2999 {
3000   int c;
3001   sscanf(col, "#%x", &c);
3002   c = c >> 4*n & 255;
3003   return c/255.;
3004 }
3005
3006 void
3007 SetPen (cairo_t *cr, float w, char *col, int dash)
3008 {
3009   static const double dotted[] = {4.0, 4.0};
3010   static int len  = sizeof(dotted) / sizeof(dotted[0]);
3011   cairo_set_line_width (cr, w);
3012   cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
3013   if(dash) cairo_set_dash (cr, dotted, len, 0.0);
3014 }
3015
3016 void DrawSeekAxis( int x, int y, int xTo, int yTo )
3017 {
3018     cairo_t *cr;
3019
3020     /* get a cairo_t */
3021     cr = cairo_create (csBoardWindow);
3022
3023     cairo_move_to (cr, x, y);
3024     cairo_line_to(cr, xTo, yTo );
3025
3026     SetPen(cr, 2, "#000000", 0);
3027     cairo_stroke(cr);
3028
3029     /* free memory */
3030     cairo_destroy (cr);
3031 }
3032
3033 void DrawSeekBackground( int left, int top, int right, int bottom )
3034 {
3035     cairo_t *cr = cairo_create (csBoardWindow);
3036
3037     cairo_rectangle (cr, left, top, right-left, bottom-top);
3038
3039     cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
3040     cairo_fill(cr);
3041
3042     /* free memory */
3043     cairo_destroy (cr);
3044 }
3045
3046 void DrawSeekText(char *buf, int x, int y)
3047 {
3048     cairo_t *cr = cairo_create (csBoardWindow);
3049
3050     cairo_select_font_face (cr, "Sans",
3051                             CAIRO_FONT_SLANT_NORMAL,
3052                             CAIRO_FONT_WEIGHT_NORMAL);
3053
3054     cairo_set_font_size (cr, 12.0);
3055
3056     cairo_move_to (cr, x, y+4);
3057     cairo_show_text( cr, buf);
3058
3059     cairo_set_source_rgba(cr, 0, 0, 0,1.0);
3060     cairo_stroke(cr);
3061
3062     /* free memory */
3063     cairo_destroy (cr);
3064 }
3065
3066 void DrawSeekDot(int x, int y, int colorNr)
3067 {
3068     cairo_t *cr = cairo_create (csBoardWindow);
3069     int square = colorNr & 0x80;
3070     colorNr &= 0x7F;
3071
3072     if(square)
3073         cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3074     else
3075         cairo_arc(cr, x, y, squareSize/8, 0.0, 2*M_PI);
3076
3077     SetPen(cr, 2, "#000000", 0);
3078     cairo_stroke_preserve(cr);
3079     switch (colorNr) {
3080       case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
3081       case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
3082       default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
3083     }
3084     cairo_fill(cr);
3085
3086     /* free memory */
3087     cairo_destroy (cr);
3088 }
3089
3090 void
3091 DrawSeekOpen ()
3092 {
3093     int boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3094     int boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3095     if(!csBoardWindow) {
3096         csBoardWindow = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), boardWidth, boardHeight);
3097         csBoardBackup = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, boardWidth, boardHeight);
3098     }
3099 }
3100
3101 void
3102 DrawSeekClose ()
3103 {
3104 }
3105
3106 void
3107 DoDrawGrid(cairo_surface_t *cs)
3108 {
3109   /* draws a grid starting around Nx, Ny squares starting at x,y */
3110   int i;
3111   cairo_t *cr;
3112
3113   DrawSeekOpen();
3114   /* get a cairo_t */
3115   cr = cairo_create (cs);
3116
3117   cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
3118   SetPen(cr, lineGap, "#000000", 0);
3119
3120   /* lines in X */
3121   for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
3122     {
3123       cairo_move_to (cr, gridSegments[i].x1, gridSegments[i].y1);
3124       cairo_line_to (cr, gridSegments[i].x2, gridSegments[i].y2);
3125       cairo_stroke (cr);
3126     }
3127
3128   /* free memory */
3129   cairo_destroy (cr);
3130
3131   return;
3132 }
3133
3134 void
3135 DrawGrid()
3136 {
3137   DoDrawGrid(csBoardWindow);
3138   DoDrawGrid(csBoardBackup);
3139 }
3140
3141 /*
3142  * event handler for redrawing the board
3143  */
3144 void
3145 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3146 {
3147     DrawPosition(True, NULL);
3148 }
3149
3150
3151 void
3152 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3153 {   // [HGM] pv: walk PV
3154     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3155 }
3156
3157 static int savedIndex;  /* gross that this is global */
3158
3159 void
3160 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3161 {
3162         String val;
3163         XawTextPosition index, dummy;
3164         Arg arg;
3165
3166         XawTextGetSelectionPos(w, &index, &dummy);
3167         XtSetArg(arg, XtNstring, &val);
3168         XtGetValues(w, &arg, 1);
3169         ReplaceComment(savedIndex, val);
3170         if(savedIndex != currentMove) ToNrEvent(savedIndex);
3171         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3172 }
3173
3174 void
3175 EditCommentPopUp (int index, char *title, char *text)
3176 {
3177     savedIndex = index;
3178     if (text == NULL) text = "";
3179     NewCommentPopup(title, text, index);
3180 }
3181
3182 void
3183 CommentPopUp (char *title, char *text)
3184 {
3185     savedIndex = currentMove; // [HGM] vari
3186     NewCommentPopup(title, text, currentMove);
3187 }
3188
3189 void
3190 CommentPopDown ()
3191 {
3192     PopDown(CommentDlg);
3193 }
3194
3195
3196 /* Disable all user input other than deleting the window */
3197 static int frozen = 0;
3198
3199 void
3200 FreezeUI ()
3201 {
3202   if (frozen) return;
3203   /* Grab by a widget that doesn't accept input */
3204   XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
3205   frozen = 1;
3206 }
3207
3208 /* Undo a FreezeUI */
3209 void
3210 ThawUI ()
3211 {
3212   if (!frozen) return;
3213   XtRemoveGrab(optList[W_MESSG].handle);
3214   frozen = 0;
3215 }
3216
3217 void
3218 ModeHighlight ()
3219 {
3220     Arg args[16];
3221     static int oldPausing = FALSE;
3222     static GameMode oldmode = (GameMode) -1;
3223     char *wname;
3224
3225     if (!boardWidget || !XtIsRealized(boardWidget)) return;
3226
3227     if (pausing != oldPausing) {
3228         oldPausing = pausing;
3229         MarkMenuItem("Mode.Pause", pausing);
3230
3231         if (appData.showButtonBar) {
3232           /* Always toggle, don't set.  Previous code messes up when
3233              invoked while the button is pressed, as releasing it
3234              toggles the state again. */
3235           {
3236             Pixel oldbg, oldfg;
3237             XtSetArg(args[0], XtNbackground, &oldbg);
3238             XtSetArg(args[1], XtNforeground, &oldfg);
3239             XtGetValues(optList[W_PAUSE].handle,
3240                         args, 2);
3241             XtSetArg(args[0], XtNbackground, oldfg);
3242             XtSetArg(args[1], XtNforeground, oldbg);
3243           }
3244           XtSetValues(optList[W_PAUSE].handle, args, 2);
3245         }
3246     }
3247
3248     wname = ModeToWidgetName(oldmode);
3249     if (wname != NULL) {
3250         MarkMenuItem(wname, False);
3251     }
3252     wname = ModeToWidgetName(gameMode);
3253     if (wname != NULL) {
3254         MarkMenuItem(wname, True);
3255     }
3256     oldmode = gameMode;
3257     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
3258
3259     /* Maybe all the enables should be handled here, not just this one */
3260     EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
3261
3262     DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
3263 }
3264
3265
3266 /*
3267  * Button/menu procedures
3268  */
3269
3270 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3271 char *selected_fen_position=NULL;
3272
3273 Boolean
3274 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3275                        Atom *type_return, XtPointer *value_return,
3276                        unsigned long *length_return, int *format_return)
3277 {
3278   char *selection_tmp;
3279
3280 //  if (!selected_fen_position) return False; /* should never happen */
3281   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3282    if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3283     FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3284     long len;
3285     size_t count;
3286     if (f == NULL) return False;
3287     fseek(f, 0, 2);
3288     len = ftell(f);
3289     rewind(f);
3290     selection_tmp = XtMalloc(len + 1);
3291     count = fread(selection_tmp, 1, len, f);
3292     fclose(f);
3293     if (len != count) {
3294       XtFree(selection_tmp);
3295       return False;
3296     }
3297     selection_tmp[len] = NULLCHAR;
3298    } else {
3299     /* note: since no XtSelectionDoneProc was registered, Xt will
3300      * automatically call XtFree on the value returned.  So have to
3301      * make a copy of it allocated with XtMalloc */
3302     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3303     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3304    }
3305
3306     *value_return=selection_tmp;
3307     *length_return=strlen(selection_tmp);
3308     *type_return=*target;
3309     *format_return = 8; /* bits per byte */
3310     return True;
3311   } else if (*target == XA_TARGETS(xDisplay)) {
3312     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3313     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3314     targets_tmp[1] = XA_STRING;
3315     *value_return = targets_tmp;
3316     *type_return = XA_ATOM;
3317     *length_return = 2;
3318 #if 0
3319     // This code leads to a read of value_return out of bounds on 64-bit systems.
3320     // Other code which I have seen always sets *format_return to 32 independent of
3321     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3322     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3323     *format_return = 8 * sizeof(Atom);
3324     if (*format_return > 32) {
3325       *length_return *= *format_return / 32;
3326       *format_return = 32;
3327     }
3328 #else
3329     *format_return = 32;
3330 #endif
3331     return True;
3332   } else {
3333     return False;
3334   }
3335 }
3336
3337 /* note: when called from menu all parameters are NULL, so no clue what the
3338  * Widget which was clicked on was, or what the click event was
3339  */
3340 void
3341 CopySomething (char *src)
3342 {
3343     selected_fen_position = src;
3344     /*
3345      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3346      * have a notion of a position that is selected but not copied.
3347      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3348      */
3349     XtOwnSelection(menuBarWidget, XA_PRIMARY,
3350                    CurrentTime,
3351                    SendPositionSelection,
3352                    NULL/* lose_ownership_proc */ ,
3353                    NULL/* transfer_done_proc */);
3354     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3355                    CurrentTime,
3356                    SendPositionSelection,
3357                    NULL/* lose_ownership_proc */ ,
3358                    NULL/* transfer_done_proc */);
3359 }
3360
3361 /* function called when the data to Paste is ready */
3362 static void
3363 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3364                  Atom *type, XtPointer value, unsigned long *len, int *format)
3365 {
3366   char *fenstr=value;
3367   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3368   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3369   EditPositionPasteFEN(fenstr);
3370   XtFree(value);
3371 }
3372
3373 /* called when Paste Position button is pressed,
3374  * all parameters will be NULL */
3375 void
3376 PastePositionProc ()
3377 {
3378     XtGetSelectionValue(menuBarWidget,
3379       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3380       /* (XtSelectionCallbackProc) */ PastePositionCB,
3381       NULL, /* client_data passed to PastePositionCB */
3382
3383       /* better to use the time field from the event that triggered the
3384        * call to this function, but that isn't trivial to get
3385        */
3386       CurrentTime
3387     );
3388     return;
3389 }
3390
3391 /* note: when called from menu all parameters are NULL, so no clue what the
3392  * Widget which was clicked on was, or what the click event was
3393  */
3394 /* function called when the data to Paste is ready */
3395 static void
3396 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3397              Atom *type, XtPointer value, unsigned long *len, int *format)
3398 {
3399   FILE* f;
3400   if (value == NULL || *len == 0) {
3401     return; /* nothing had been selected to copy */
3402   }
3403   f = fopen(gamePasteFilename, "w");
3404   if (f == NULL) {
3405     DisplayError(_("Can't open temp file"), errno);
3406     return;
3407   }
3408   fwrite(value, 1, *len, f);
3409   fclose(f);
3410   XtFree(value);
3411   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3412 }
3413
3414 /* called when Paste Game button is pressed,
3415  * all parameters will be NULL */
3416 void
3417 PasteGameProc ()
3418 {
3419     XtGetSelectionValue(menuBarWidget,
3420       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3421       /* (XtSelectionCallbackProc) */ PasteGameCB,
3422       NULL, /* client_data passed to PasteGameCB */
3423
3424       /* better to use the time field from the event that triggered the
3425        * call to this function, but that isn't trivial to get
3426        */
3427       CurrentTime
3428     );
3429     return;
3430 }
3431
3432
3433 void
3434 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3435 {
3436     QuitProc();
3437 }
3438
3439 int
3440 ShiftKeys ()
3441 {   // bassic primitive for determining if modifier keys are pressed
3442     long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3443     char keys[32];
3444     int i,j,  k=0;
3445     XQueryKeymap(xDisplay,keys);
3446     for(i=0; i<6; i++) {
3447         k <<= 1;
3448         j = XKeysymToKeycode(xDisplay, codes[i]);
3449         k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3450     }
3451     return k;
3452 }
3453
3454 static void
3455 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3456 {
3457     char buf[10];
3458     KeySym sym;
3459     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3460     if ( n == 1 && *buf >= 32 // printable
3461          && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3462         ) BoxAutoPopUp (buf);
3463 }
3464
3465 static void
3466 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3467 {   // [HGM] input: let up-arrow recall previous line from history
3468     IcsKey(1);
3469 }
3470
3471 static void
3472 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3473 {   // [HGM] input: let down-arrow recall next line from history
3474     IcsKey(-1);
3475 }
3476
3477 static void
3478 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3479 {
3480     IcsKey(0);
3481 }
3482
3483 void
3484 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3485 {
3486         if (!TempBackwardActive) {
3487                 TempBackwardActive = True;
3488                 BackwardEvent();
3489         }
3490 }
3491
3492 void
3493 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3494 {
3495         /* Check to see if triggered by a key release event for a repeating key.
3496          * If so the next queued event will be a key press of the same key at the same time */
3497         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3498                 XEvent next;
3499                 XPeekEvent(xDisplay, &next);
3500                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3501                         next.xkey.keycode == event->xkey.keycode)
3502                                 return;
3503         }
3504     ForwardEvent();
3505         TempBackwardActive = False;
3506 }
3507
3508 void
3509 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3510 {   // called as key binding
3511     char buf[MSG_SIZ];
3512     String name;
3513     if (nprms && *nprms > 0)
3514       name = prms[0];
3515     else
3516       name = "xboard";
3517     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3518     system(buf);
3519 }
3520
3521 void
3522 ManProc ()
3523 {   // called from menu
3524     ManInner(NULL, NULL, NULL, NULL);
3525 }
3526
3527 void
3528 SetWindowTitle (char *text, char *title, char *icon)
3529 {
3530     Arg args[16];
3531     int i;
3532     if (appData.titleInWindow) {
3533         i = 0;
3534         XtSetArg(args[i], XtNlabel, text);   i++;
3535         XtSetValues(titleWidget, args, i);
3536     }
3537     i = 0;
3538     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
3539     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
3540     XtSetValues(shellWidget, args, i);
3541     XSync(xDisplay, False);
3542 }
3543
3544
3545 static int
3546 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3547 {
3548     return 0;
3549 }
3550
3551 void
3552 DisplayIcsInteractionTitle (String message)
3553 {
3554   if (oldICSInteractionTitle == NULL) {
3555     /* Magic to find the old window title, adapted from vim */
3556     char *wina = getenv("WINDOWID");
3557     if (wina != NULL) {
3558       Window win = (Window) atoi(wina);
3559       Window root, parent, *children;
3560       unsigned int nchildren;
3561       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3562       for (;;) {
3563         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3564         if (!XQueryTree(xDisplay, win, &root, &parent,
3565                         &children, &nchildren)) break;
3566         if (children) XFree((void *)children);
3567         if (parent == root || parent == 0) break;
3568         win = parent;
3569       }
3570       XSetErrorHandler(oldHandler);
3571     }
3572     if (oldICSInteractionTitle == NULL) {
3573       oldICSInteractionTitle = "xterm";
3574     }
3575   }
3576   printf("\033]0;%s\007", message);
3577   fflush(stdout);
3578 }
3579
3580
3581 XtIntervalId delayedEventTimerXID = 0;
3582 DelayedEventCallback delayedEventCallback = 0;
3583
3584 void
3585 FireDelayedEvent ()
3586 {
3587     delayedEventTimerXID = 0;
3588     delayedEventCallback();
3589 }
3590
3591 void
3592 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3593 {
3594     if(delayedEventTimerXID && delayedEventCallback == cb)
3595         // [HGM] alive: replace, rather than add or flush identical event
3596         XtRemoveTimeOut(delayedEventTimerXID);
3597     delayedEventCallback = cb;
3598     delayedEventTimerXID =
3599       XtAppAddTimeOut(appContext, millisec,
3600                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3601 }
3602
3603 DelayedEventCallback
3604 GetDelayedEvent ()
3605 {
3606   if (delayedEventTimerXID) {
3607     return delayedEventCallback;
3608   } else {
3609     return NULL;
3610   }
3611 }
3612
3613 void
3614 CancelDelayedEvent ()
3615 {
3616   if (delayedEventTimerXID) {
3617     XtRemoveTimeOut(delayedEventTimerXID);
3618     delayedEventTimerXID = 0;
3619   }
3620 }
3621
3622 XtIntervalId loadGameTimerXID = 0;
3623
3624 int
3625 LoadGameTimerRunning ()
3626 {
3627     return loadGameTimerXID != 0;
3628 }
3629
3630 int
3631 StopLoadGameTimer ()
3632 {
3633     if (loadGameTimerXID != 0) {
3634         XtRemoveTimeOut(loadGameTimerXID);
3635         loadGameTimerXID = 0;
3636         return TRUE;
3637     } else {
3638         return FALSE;
3639     }
3640 }
3641
3642 void
3643 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3644 {
3645     loadGameTimerXID = 0;
3646     AutoPlayGameLoop();
3647 }
3648
3649 void
3650 StartLoadGameTimer (long millisec)
3651 {
3652     loadGameTimerXID =
3653       XtAppAddTimeOut(appContext, millisec,
3654                       (XtTimerCallbackProc) LoadGameTimerCallback,
3655                       (XtPointer) 0);
3656 }
3657
3658 XtIntervalId analysisClockXID = 0;
3659
3660 void
3661 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3662 {
3663     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3664          || appData.icsEngineAnalyze) { // [DM]
3665         AnalysisPeriodicEvent(0);
3666         StartAnalysisClock();
3667     }
3668 }
3669
3670 void
3671 StartAnalysisClock ()
3672 {
3673     analysisClockXID =
3674       XtAppAddTimeOut(appContext, 2000,
3675                       (XtTimerCallbackProc) AnalysisClockCallback,
3676                       (XtPointer) 0);
3677 }
3678
3679 XtIntervalId clockTimerXID = 0;
3680
3681 int
3682 ClockTimerRunning ()
3683 {
3684     return clockTimerXID != 0;
3685 }
3686
3687 int
3688 StopClockTimer ()
3689 {
3690     if (clockTimerXID != 0) {
3691         XtRemoveTimeOut(clockTimerXID);
3692         clockTimerXID = 0;
3693         return TRUE;
3694     } else {
3695         return FALSE;
3696     }
3697 }
3698
3699 void
3700 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3701 {
3702     clockTimerXID = 0;
3703     DecrementClocks();
3704 }
3705
3706 void
3707 StartClockTimer (long millisec)
3708 {
3709     clockTimerXID =
3710       XtAppAddTimeOut(appContext, millisec,
3711                       (XtTimerCallbackProc) ClockTimerCallback,
3712                       (XtPointer) 0);
3713 }
3714
3715 void
3716 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3717 {
3718     char buf[MSG_SIZ];
3719     Arg args[16];
3720     Widget w = (Widget) opt->handle;
3721
3722     /* check for low time warning */
3723     Pixel foregroundOrWarningColor = timerForegroundPixel;
3724
3725     if (timer > 0 &&
3726         appData.lowTimeWarning &&
3727         (timer / 1000) < appData.icsAlarmTime)
3728       foregroundOrWarningColor = lowTimeWarningColor;
3729
3730     if (appData.clockMode) {
3731       snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
3732       XtSetArg(args[0], XtNlabel, buf);
3733     } else {
3734       snprintf(buf, MSG_SIZ, "%s  ", color);
3735       XtSetArg(args[0], XtNlabel, buf);
3736     }
3737
3738     if (highlight) {
3739
3740         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3741         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3742     } else {
3743         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3744         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3745     }
3746
3747     XtSetValues(w, args, 3);
3748 }
3749
3750 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3751
3752 void
3753 SetClockIcon (int color)
3754 {
3755     Arg args[16];
3756     Pixmap pm = *clockIcons[color];
3757     if (iconPixmap != pm) {
3758         iconPixmap = pm;
3759         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3760         XtSetValues(shellWidget, args, 1);
3761     }
3762 }
3763
3764 void
3765 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3766 {
3767     InputSource *is = (InputSource *) closure;
3768     int count;
3769     int error;
3770     char *p, *q;
3771
3772     if (is->lineByLine) {
3773         count = read(is->fd, is->unused,
3774                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3775         if (count <= 0) {
3776             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3777             return;
3778         }
3779         is->unused += count;
3780         p = is->buf;
3781         while (p < is->unused) {
3782             q = memchr(p, '\n', is->unused - p);
3783             if (q == NULL) break;
3784             q++;
3785             (is->func)(is, is->closure, p, q - p, 0);
3786             p = q;
3787         }
3788         q = is->buf;
3789         while (p < is->unused) {
3790             *q++ = *p++;
3791         }
3792         is->unused = q;
3793     } else {
3794         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3795         if (count == -1)
3796           error = errno;
3797         else
3798           error = 0;
3799         (is->func)(is, is->closure, is->buf, count, error);
3800     }
3801 }
3802
3803 InputSourceRef
3804 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3805 {
3806     InputSource *is;
3807     ChildProc *cp = (ChildProc *) pr;
3808
3809     is = (InputSource *) calloc(1, sizeof(InputSource));
3810     is->lineByLine = lineByLine;
3811     is->func = func;
3812     if (pr == NoProc) {
3813         is->kind = CPReal;
3814         is->fd = fileno(stdin);
3815     } else {
3816         is->kind = cp->kind;
3817         is->fd = cp->fdFrom;
3818     }
3819     if (lineByLine) {
3820         is->unused = is->buf;
3821     }
3822
3823     is->xid = XtAppAddInput(appContext, is->fd,
3824                             (XtPointer) (XtInputReadMask),
3825                             (XtInputCallbackProc) DoInputCallback,
3826                             (XtPointer) is);
3827     is->closure = closure;
3828     return (InputSourceRef) is;
3829 }
3830
3831 void
3832 RemoveInputSource (InputSourceRef isr)
3833 {
3834     InputSource *is = (InputSource *) isr;
3835
3836     if (is->xid == 0) return;
3837     XtRemoveInput(is->xid);
3838     is->xid = 0;
3839 }
3840
3841 /****   Animation code by Hugh Fisher, DCS, ANU. ****/
3842
3843 /*      Masks for XPM pieces. Black and white pieces can have
3844         different shapes, but in the interest of retaining my
3845         sanity pieces must have the same outline on both light
3846         and dark squares, and all pieces must use the same
3847         background square colors/images.                */
3848
3849 static int xpmDone = 0;
3850 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3851 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3852 static cairo_surface_t *c_animBufs[3*NrOfAnims]; // newBuf, saveBuf
3853
3854 static void
3855 CreateAnimMasks (int pieceDepth)
3856 {
3857   ChessSquare   piece;
3858   Pixmap        buf;
3859   GC            bufGC, maskGC;
3860   int           kind, n;
3861   unsigned long plane;
3862   XGCValues     values;
3863
3864   /* Need a bitmap just to get a GC with right depth */
3865   buf = XCreatePixmap(xDisplay, xBoardWindow,
3866                         8, 8, 1);
3867   values.foreground = 1;
3868   values.background = 0;
3869   /* Don't use XtGetGC, not read only */
3870   maskGC = XCreateGC(xDisplay, buf,
3871                     GCForeground | GCBackground, &values);
3872   XFreePixmap(xDisplay, buf);
3873
3874   buf = XCreatePixmap(xDisplay, xBoardWindow,
3875                       squareSize, squareSize, pieceDepth);
3876   values.foreground = XBlackPixel(xDisplay, xScreen);
3877   values.background = XWhitePixel(xDisplay, xScreen);
3878   bufGC = XCreateGC(xDisplay, buf,
3879                     GCForeground | GCBackground, &values);
3880
3881   for (piece = WhitePawn; piece <= BlackKing; piece++) {
3882     /* Begin with empty mask */
3883     if(!xpmDone) // [HGM] pieces: keep using existing
3884     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3885                                  squareSize, squareSize, 1);
3886     XSetFunction(xDisplay, maskGC, GXclear);
3887     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3888                    0, 0, squareSize, squareSize);
3889
3890     /* Take a copy of the piece */
3891     if (White(piece))
3892       kind = 0;
3893     else
3894       kind = 2;
3895     XSetFunction(xDisplay, bufGC, GXcopy);
3896     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3897               buf, bufGC,
3898               0, 0, squareSize, squareSize, 0, 0);
3899
3900     /* XOR the background (light) over the piece */
3901     XSetFunction(xDisplay, bufGC, GXxor);
3902     if (useImageSqs)
3903       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3904                 0, 0, squareSize, squareSize, 0, 0);
3905     else {
3906       XSetForeground(xDisplay, bufGC, lightSquareColor);
3907       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3908     }
3909
3910     /* We now have an inverted piece image with the background
3911        erased. Construct mask by just selecting all the non-zero
3912        pixels - no need to reconstruct the original image.      */
3913     XSetFunction(xDisplay, maskGC, GXor);
3914     plane = 1;
3915     /* Might be quicker to download an XImage and create bitmap
3916        data from it rather than this N copies per piece, but it
3917        only takes a fraction of a second and there is a much
3918        longer delay for loading the pieces.             */
3919     for (n = 0; n < pieceDepth; n ++) {
3920       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3921                  0, 0, squareSize, squareSize,
3922                  0, 0, plane);
3923       plane = plane << 1;
3924     }
3925   }
3926   /* Clean up */
3927   XFreePixmap(xDisplay, buf);
3928   XFreeGC(xDisplay, bufGC);
3929   XFreeGC(xDisplay, maskGC);
3930 }
3931
3932 static void
3933 InitAnimState (AnimNr anr, XWindowAttributes *info)
3934 {
3935   XtGCMask  mask;
3936   XGCValues values;
3937
3938   if(cairoAnimate) {
3939     DrawSeekOpen(); // set cs to board widget
3940     if(c_animBufs[anr]) cairo_surface_destroy (c_animBufs[anr]);
3941     if(c_animBufs[anr+2]) cairo_surface_destroy (c_animBufs[anr+2]);
3942     c_animBufs[anr+4] = csBoardWindow;
3943     c_animBufs[anr+2] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3944     c_animBufs[anr] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3945   }
3946
3947   /* Each buffer is square size, same depth as window */
3948   animBufs[anr+4] = xBoardWindow;
3949   animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3950                         squareSize, squareSize, info->depth);
3951   animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3952                         squareSize, squareSize, info->depth);
3953
3954   /* Create a plain GC for blitting */
3955   mask = GCForeground | GCBackground | GCFunction |
3956          GCPlaneMask | GCGraphicsExposures;
3957   values.foreground = XBlackPixel(xDisplay, xScreen);
3958   values.background = XWhitePixel(xDisplay, xScreen);
3959   values.function   = GXcopy;
3960   values.plane_mask = AllPlanes;
3961   values.graphics_exposures = False;
3962   animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3963
3964   /* Piece will be copied from an existing context at
3965      the start of each new animation/drag. */
3966   animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3967
3968   /* Outline will be a read-only copy of an existing */
3969   animGCs[anr+4] = None;
3970 }
3971
3972 void
3973 CreateAnimVars ()
3974 {
3975   XWindowAttributes info;
3976
3977   if (!cairoAnimate && xpmDone && gameInfo.variant == oldVariant) return;
3978   if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3979   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3980
3981   InitAnimState(Game, &info);
3982   InitAnimState(Player, &info);
3983
3984   /* For XPM pieces, we need bitmaps to use as masks. */
3985   if (useImages & !xpmDone)
3986     CreateAnimMasks(info.depth), xpmDone = 1;
3987 }
3988
3989 #ifndef HAVE_USLEEP
3990
3991 static Boolean frameWaiting;
3992
3993 static RETSIGTYPE
3994 FrameAlarm (int sig)
3995 {
3996   frameWaiting = False;
3997   /* In case System-V style signals.  Needed?? */
3998   signal(SIGALRM, FrameAlarm);
3999 }
4000
4001 void
4002 FrameDelay (int time)
4003 {
4004   struct itimerval delay;
4005
4006   XSync(xDisplay, False);
4007
4008   if (time > 0) {
4009     frameWaiting = True;
4010     signal(SIGALRM, FrameAlarm);
4011     delay.it_interval.tv_sec =
4012       delay.it_value.tv_sec = time / 1000;
4013     delay.it_interval.tv_usec =
4014       delay.it_value.tv_usec = (time % 1000) * 1000;
4015     setitimer(ITIMER_REAL, &delay, NULL);
4016     while (frameWaiting) pause();
4017     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
4018     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
4019     setitimer(ITIMER_REAL, &delay, NULL);
4020   }
4021 }
4022
4023 #else
4024
4025 void
4026 FrameDelay (int time)
4027 {
4028   XSync(xDisplay, False);
4029   if (time > 0)
4030     usleep(time * 1000);
4031 }
4032
4033 #endif
4034
4035 static void
4036 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
4037 {
4038   GC source;
4039
4040   /* Bitmap for piece being moved. */
4041   if (appData.monoMode) {
4042       *mask = *pieceToSolid(piece);
4043   } else if (useImages) {
4044 #if HAVE_LIBXPM
4045       *mask = xpmMask[piece];
4046 #else
4047       *mask = ximMaskPm[piece];
4048 #endif
4049   } else {
4050       *mask = *pieceToSolid(piece);
4051   }
4052
4053   /* GC for piece being moved. Square color doesn't matter, but
4054      since it gets modified we make a copy of the original. */
4055   if (White(piece)) {
4056     if (appData.monoMode)
4057       source = bwPieceGC;
4058     else
4059       source = wlPieceGC;
4060   } else {
4061     if (appData.monoMode)
4062       source = wbPieceGC;
4063     else
4064       source = blPieceGC;
4065   }
4066   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
4067
4068   /* Outline only used in mono mode and is not modified */
4069   if (White(piece))
4070     *outline = bwPieceGC;
4071   else
4072     *outline = wbPieceGC;
4073 }
4074
4075 static void
4076 OverlayPiece (ChessSquare piece, GC clip, GC outline,  Drawable dest)
4077 {
4078   int   kind;
4079
4080   if (!useImages) {
4081     /* Draw solid rectangle which will be clipped to shape of piece */
4082     XFillRectangle(xDisplay, dest, clip,
4083                    0, 0, squareSize, squareSize);
4084     if (appData.monoMode)
4085       /* Also draw outline in contrasting color for black
4086          on black / white on white cases                */
4087       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
4088                  0, 0, squareSize, squareSize, 0, 0, 1);
4089   } else {
4090     /* Copy the piece */
4091     if (White(piece))
4092       kind = 0;
4093     else
4094       kind = 2;
4095     if(appData.upsideDown && flipView) kind ^= 2;
4096     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4097               dest, clip,
4098               0, 0, squareSize, squareSize,
4099               0, 0);
4100   }
4101 }
4102
4103 static void
4104 CairoOverlayPiece (ChessSquare piece, cairo_surface_t *dest)
4105 {
4106   static ChessSquare oldPiece = -1;
4107   static int oldSize;
4108   static cairo_t *pieceSource;
4109   if(piece != oldPiece || squareSize != oldSize) { // try make it faster by only changing cr if we need other piece
4110     if(pieceSource) cairo_destroy (pieceSource);
4111     pieceSource = cairo_create (dest);
4112     cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
4113     oldPiece = piece; oldSize = squareSize;
4114   }
4115   cairo_paint(pieceSource);
4116 }
4117
4118 void
4119 InsertPiece (AnimNr anr, ChessSquare piece)
4120 {
4121   if(cairoAnimate) {
4122     CairoOverlayPiece(piece, c_animBufs[anr]);
4123   } else
4124   OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
4125 }
4126
4127 void
4128 DrawBlank (AnimNr anr, int x, int y, int startColor)
4129 {
4130     if(cairoAnimate)
4131     BlankSquare(x, y, startColor, EmptySquare, (Drawable) c_animBufs[anr+2], 0);
4132     else
4133     BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
4134 }
4135
4136 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
4137                  int srcX, int srcY, int width, int height, int destX, int destY)
4138 {
4139     if(cairoAnimate) {
4140         cairo_t *cr;// = cairo_create (c_animBufs[anr+destBuf]);
4141         cr = cairo_create (c_animBufs[anr+destBuf]);
4142         if(c_animBufs[anr+srcBuf] == csBoardWindow)
4143         cairo_set_source_surface (cr, csBoardBackup, destX - srcX, destY - srcY);
4144         else
4145         cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
4146         cairo_rectangle (cr, destX, destY, width, height);
4147         cairo_fill (cr);
4148         cairo_destroy (cr);
4149         if(c_animBufs[anr+destBuf] == csBoardWindow) {
4150         cr = cairo_create (csBoardBackup); // also draw to backup
4151         cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
4152         cairo_rectangle (cr, destX, destY, width, height);
4153         cairo_fill (cr);
4154         cairo_destroy (cr);
4155         }
4156     } else
4157     XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
4158                 srcX, srcY, width, height, destX, destY);
4159 }
4160
4161 void
4162 SetDragPiece (AnimNr anr, ChessSquare piece)
4163 {
4164   Pixmap mask;
4165   if(cairoAnimate) return;
4166   /* The piece will be drawn using its own bitmap as a matte    */
4167   SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
4168   XSetClipMask(xDisplay, animGCs[anr+2], mask);
4169 }
4170
4171 /* [AS] Arrow highlighting support */
4172
4173 void DrawPolygon(Pnt arrow[], int nr)
4174 {   // for now on own surface; eventually this should become a global that is only destroyed on resize
4175     cairo_surface_t *boardSurface;
4176     cairo_t *cr;
4177     int i;
4178     int w = lineGap + BOARD_WIDTH * (squareSize + lineGap);
4179     int h = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
4180     boardSurface = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), w, h);
4181     cr = cairo_create (boardSurface);
4182     cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
4183     for (i=0;i<nr;i++) {
4184         cairo_line_to(cr, arrow[i].x, arrow[i].y);
4185     }
4186     if(appData.monoMode) { // should we always outline arrow?
4187         cairo_line_to(cr, arrow[0].x, arrow[0].y);
4188         SetPen(cr, 2, "#000000", 0);
4189         cairo_stroke_preserve(cr);
4190     }
4191     SetPen(cr, 2, appData.highlightSquareColor, 0);
4192     cairo_fill(cr);
4193
4194     /* free memory */
4195     cairo_destroy (cr);
4196     cairo_surface_destroy (boardSurface);
4197 }
4198
4199 static void
4200 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
4201 {
4202     char buf[MSG_SIZ], *logoName = buf;
4203     if(appData.logo[n][0]) {
4204         logoName = appData.logo[n];
4205     } else if(appData.autoLogo) {
4206         if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
4207             sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
4208         } else if(appData.directory[n] && appData.directory[n][0]) {
4209             sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
4210         }
4211     }
4212     if(logoName[0])
4213         { ASSIGN(cps->programLogo, logoName); }
4214 }
4215
4216 void
4217 UpdateLogos (int displ)
4218 {
4219     if(optList[W_WHITE-1].handle == NULL) return;
4220     LoadLogo(&first, 0, 0);
4221     LoadLogo(&second, 1, appData.icsActive);
4222     if(displ) DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
4223     return;
4224 }
4225