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