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