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