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