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