Cleanup CairoOverlayPiece
[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     char *col;
2538
2539     switch(type) {
2540         case 0: col = "#000000"; break;
2541         case 1: col = appData.highlightSquareColor; break;
2542         case 2: col = appData.premoveHighlightColor; break;
2543     }
2544     cr = cairo_create(cs);
2545     cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
2546     cairo_rectangle(cr, x, y, squareSize+lineGap, squareSize+lineGap);
2547     SetPen(cr, lineGap, col, 0);
2548     cairo_stroke(cr);
2549 }
2550
2551 void
2552 DrawBorder (int x, int y, int type)
2553 {
2554   DoDrawBorder(csBoardWindow, x, y, type);
2555   DoDrawBorder(csBoardBackup, x, y, type);
2556 }
2557
2558 static int
2559 CutOutSquare (int x, int y, int *x0, int *y0, int  kind)
2560 {
2561     int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2562     int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2563     *x0 = 0; *y0 = 0;
2564     if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2565     if(textureW[kind] < W*squareSize)
2566         *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2567     else
2568         *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2569     if(textureH[kind] < H*squareSize)
2570         *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2571     else
2572         *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2573     return 1;
2574 }
2575
2576 void
2577 DrawLogo (void *handle, void *logo)
2578 {
2579     cairo_surface_t *img, *cs;
2580     cairo_t *cr;
2581     int w, h;
2582
2583     if(!logo || !handle) return;
2584     cs = cairo_xlib_surface_create(xDisplay, XtWindow(handle), DefaultVisual(xDisplay, 0), appData.logoSize, appData.logoSize/2);
2585     img = cairo_image_surface_create_from_png (logo);
2586     w = cairo_image_surface_get_width (img);
2587     h = cairo_image_surface_get_height (img);
2588     cr = cairo_create(cs);
2589     cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
2590     cairo_set_source_surface (cr, img, 0, 0);
2591     cairo_paint (cr);
2592     cairo_destroy (cr);
2593     cairo_surface_destroy (img);
2594     cairo_surface_destroy (cs);
2595 }
2596
2597 static void
2598 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2599 {   // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2600     int x0, y0;
2601     if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2602         if(pngBoardBitmap[color]) {
2603             cairo_t *cr;
2604             if(!fac && !cairoAnimate) return;
2605             DrawSeekOpen();
2606             cr = cairo_create (fac ? csBoardWindow : (cairo_surface_t *) dest);
2607             cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2608             cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2609             cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2610             cairo_fill (cr);
2611             cairo_destroy (cr);
2612            if(fac) {
2613             cr = cairo_create (csBoardBackup);
2614             cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2615             cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2616             cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2617             cairo_fill (cr);
2618             cairo_destroy (cr);
2619            }
2620         } else
2621         XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2622                   squareSize, squareSize, x*fac, y*fac);
2623     } else
2624     if(csBoardWindow) {
2625         cairo_t *cr = cairo_create (csBoardWindow);
2626         char *col;
2627         switch (color) {
2628           case 0: col = appData.darkSquareColor; break;
2629           case 1: col = appData.lightSquareColor; break;
2630           case 2: col = "#000000"; break;
2631         }
2632         SetPen(cr, 2.0, col, 0);
2633         cairo_rectangle (cr, x, y, squareSize, squareSize);
2634         cairo_fill (cr);
2635         cairo_destroy (cr);
2636         cr = cairo_create (csBoardBackup);
2637         SetPen(cr, 2.0, col, 0);
2638         cairo_rectangle (cr, x, y, squareSize, squareSize);
2639         cairo_fill (cr);
2640         cairo_destroy (cr);
2641     } else
2642     if (useImages && useImageSqs) {
2643         Pixmap pm;
2644         switch (color) {
2645           case 1: /* light */
2646             pm = xpmLightSquare;
2647             break;
2648           case 0: /* dark */
2649             pm = xpmDarkSquare;
2650             break;
2651           case 2: /* neutral */
2652           default:
2653             pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2654             break;
2655         }
2656         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2657                   squareSize, squareSize, x*fac, y*fac);
2658     } else {
2659         GC gc;
2660         switch (color) {
2661           case 1: /* light */
2662             gc = lightSquareGC;
2663             break;
2664           case 0: /* dark */
2665             gc = darkSquareGC;
2666             break;
2667           case 2: /* neutral */
2668           default:
2669             gc = lineGC;
2670             break;
2671         }
2672         XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2673     }
2674 }
2675
2676 /*
2677    I split out the routines to draw a piece so that I could
2678    make a generic flash routine.
2679 */
2680 static void
2681 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2682 {
2683     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2684     switch (square_color) {
2685       case 1: /* light */
2686       case 2: /* neutral */
2687       default:
2688         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2689                   ? *pieceToOutline(piece)
2690                   : *pieceToSolid(piece),
2691                   dest, bwPieceGC, 0, 0,
2692                   squareSize, squareSize, x, y);
2693         break;
2694       case 0: /* dark */
2695         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2696                   ? *pieceToSolid(piece)
2697                   : *pieceToOutline(piece),
2698                   dest, wbPieceGC, 0, 0,
2699                   squareSize, squareSize, x, y);
2700         break;
2701     }
2702 }
2703
2704 static void
2705 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2706 {
2707     switch (square_color) {
2708       case 1: /* light */
2709       case 2: /* neutral */
2710       default:
2711         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2712                    ? *pieceToOutline(piece)
2713                    : *pieceToSolid(piece),
2714                    dest, bwPieceGC, 0, 0,
2715                    squareSize, squareSize, x, y, 1);
2716         break;
2717       case 0: /* dark */
2718         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2719                    ? *pieceToSolid(piece)
2720                    : *pieceToOutline(piece),
2721                    dest, wbPieceGC, 0, 0,
2722                    squareSize, squareSize, x, y, 1);
2723         break;
2724     }
2725 }
2726
2727 static void
2728 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2729 {
2730     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2731     switch (square_color) {
2732       case 1: /* light */
2733         XCopyPlane(xDisplay, *pieceToSolid(piece),
2734                    dest, (int) piece < (int) BlackPawn
2735                    ? wlPieceGC : blPieceGC, 0, 0,
2736                    squareSize, squareSize, x, y, 1);
2737         break;
2738       case 0: /* dark */
2739         XCopyPlane(xDisplay, *pieceToSolid(piece),
2740                    dest, (int) piece < (int) BlackPawn
2741                    ? wdPieceGC : bdPieceGC, 0, 0,
2742                    squareSize, squareSize, x, y, 1);
2743         break;
2744       case 2: /* neutral */
2745       default:
2746         break; // should never contain pieces
2747     }
2748 }
2749
2750 static void
2751 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2752 {
2753     int kind, p = piece;
2754
2755     switch (square_color) {
2756       case 1: /* light */
2757       case 2: /* neutral */
2758       default:
2759         if ((int)piece < (int) BlackPawn) {
2760             kind = 0;
2761         } else {
2762             kind = 2;
2763             piece -= BlackPawn;
2764         }
2765         break;
2766       case 0: /* dark */
2767         if ((int)piece < (int) BlackPawn) {
2768             kind = 1;
2769         } else {
2770             kind = 3;
2771             piece -= BlackPawn;
2772         }
2773         break;
2774     }
2775     if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2776     if(useTexture & square_color+1) {
2777         BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2778         XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2779         XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2780         XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2781         XSetClipMask(xDisplay, wlPieceGC, None);
2782         XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2783     } else
2784     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2785               dest, wlPieceGC, 0, 0,
2786               squareSize, squareSize, x, y);
2787 }
2788
2789 static void
2790 pngDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2791 {
2792     int kind, p = piece;
2793     cairo_t *cr;
2794
2795     if ((int)piece < (int) BlackPawn) {
2796         kind = 0;
2797     } else {
2798         kind = 1;
2799         piece -= BlackPawn;
2800     }
2801     if(appData.upsideDown && flipView) { p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2802     BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2803     DrawSeekOpen();
2804     cr = cairo_create (csBoardWindow);
2805     cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2806     cairo_paint(cr);
2807     cairo_destroy (cr);
2808     cr = cairo_create (csBoardBackup);
2809     cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2810     cairo_paint(cr);
2811     cairo_destroy (cr);
2812 }
2813
2814 typedef void (*DrawFunc)();
2815
2816 DrawFunc
2817 ChooseDrawFunc ()
2818 {
2819     if (appData.monoMode) {
2820         if (DefaultDepth(xDisplay, xScreen) == 1) {
2821             return monoDrawPiece_1bit;
2822         } else {
2823             return monoDrawPiece;
2824         }
2825     } else if(appData.pngDirectory[0]) {
2826         return pngDrawPiece;
2827     } else {
2828         if (useImages)
2829           return colorDrawPieceImage;
2830         else
2831           return colorDrawPiece;
2832     }
2833 }
2834
2835 void
2836 DoDrawDot (int marker, int x, int y, int r, cairo_surface_t *cs)
2837 {
2838         cairo_t *cr;
2839         DrawSeekOpen();
2840         cr = cairo_create(cs);
2841         cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
2842         if(appData.monoMode) {
2843             SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
2844             cairo_stroke_preserve(cr);
2845             SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
2846         } else {
2847             SetPen(cr, 2, marker == 2 ? "#FF0000" : "#FFFF00", 0);
2848         }
2849         cairo_fill(cr);
2850         cairo_stroke(cr);
2851
2852         cairo_destroy(cr);
2853 }
2854
2855 void
2856 DrawDot (int marker, int x, int y, int r)
2857 {
2858   DoDrawDot(marker, x, y, r, csBoardWindow);
2859   DoDrawDot(marker, x, y, r, csBoardBackup);
2860 }
2861
2862 void
2863 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2864 {   // basic front-end board-draw function: takes care of everything that can be in square:
2865     // piece, background, coordinate/count, marker dot
2866     int direction, font_ascent, font_descent;
2867     XCharStruct overall;
2868     DrawFunc drawfunc;
2869
2870     if (piece == EmptySquare) {
2871         BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2872     } else {
2873         drawfunc = ChooseDrawFunc();
2874         drawfunc(piece, square_color, x, y, xBoardWindow);
2875     }
2876
2877     if(align) { // square carries inscription (coord or piece count)
2878         int xx = x, yy = y;
2879         GC hGC = align < 3 ? coordGC : countGC;
2880         // first calculate where it goes
2881         XTextExtents(countFontStruct, string, 1, &direction,
2882                          &font_ascent, &font_descent, &overall);
2883         if (align == 1) {
2884             xx += squareSize - overall.width - 2;
2885             yy += squareSize - font_descent - 1;
2886         } else if (align == 2) {
2887             xx += 2, yy += font_ascent + 1;
2888         } else if (align == 3) {
2889             xx += squareSize - overall.width - 2;
2890             yy += font_ascent + 1;
2891         } else if (align == 4) {
2892             xx += 2, yy += font_ascent + 1;
2893         }
2894         // then draw it
2895         if (appData.monoMode) {
2896             XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2897         } else {
2898             if(*appData.pngDirectory) {
2899                 cairo_t *cr = cairo_create (csBoardWindow);
2900                 cairo_select_font_face (cr, "Sans",
2901                             CAIRO_FONT_SLANT_NORMAL,
2902                             CAIRO_FONT_WEIGHT_BOLD);
2903
2904                 cairo_set_font_size (cr, squareSize/4);
2905
2906                 cairo_move_to (cr, xx-1, yy);
2907                 if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2908                 else          cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
2909                 cairo_show_text (cr, string);
2910                 cairo_destroy (cr);
2911                 cr = cairo_create (csBoardBackup);
2912                 cairo_select_font_face (cr, "Sans",
2913                             CAIRO_FONT_SLANT_NORMAL,
2914                             CAIRO_FONT_WEIGHT_BOLD);
2915
2916                 cairo_set_font_size (cr, squareSize/4);
2917
2918                 cairo_move_to (cr, xx-1, yy);
2919                 if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2920                 else          cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
2921                 cairo_show_text (cr, string);
2922                 cairo_destroy (cr);
2923             } else
2924             XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2925         }
2926     }
2927
2928     if(marker) { // print fat marker dot, if requested
2929         DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2930     }
2931 }
2932
2933 void
2934 FlashDelay (int flash_delay)
2935 {
2936         XSync(xDisplay, False);
2937         if(flash_delay) do_flash_delay(flash_delay);
2938 }
2939
2940 double
2941 Fraction (int x, int start, int stop)
2942 {
2943    double f = ((double) x - start)/(stop - start);
2944    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2945    return f;
2946 }
2947
2948 static WindowPlacement wpNew;
2949
2950 void
2951 CoDrag (Widget sh, WindowPlacement *wp)
2952 {
2953     Arg args[16];
2954     int j=0, touch=0, fudge = 2;
2955     GetActualPlacement(sh, wp);
2956     if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x)         < fudge) touch = 1; else // right touch
2957     if(abs(wp->x + wp->width + 2*frameX - wpMain.x)            < fudge) touch = 2; else // left touch
2958     if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2959     if(abs(wp->y + wp->height + frameX + frameY - wpMain.y)    < fudge) touch = 4;      // top touch
2960     if(!touch ) return; // only windows that touch co-move
2961     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2962         int heightInc = wpNew.height - wpMain.height;
2963         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2964         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2965         wp->y += fracTop * heightInc;
2966         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2967         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2968     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2969         int widthInc = wpNew.width - wpMain.width;
2970         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2971         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2972         wp->y += fracLeft * widthInc;
2973         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2974         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2975     }
2976     wp->x += wpNew.x - wpMain.x;
2977     wp->y += wpNew.y - wpMain.y;
2978     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2979     if(touch == 3) wp->y += wpNew.height - wpMain.height;
2980     XtSetArg(args[j], XtNx, wp->x); j++;
2981     XtSetArg(args[j], XtNy, wp->y); j++;
2982     XtSetValues(sh, args, j);
2983 }
2984
2985 void
2986 ReSize (WindowPlacement *wp)
2987 {
2988         int sqx, sqy;
2989         if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
2990         sqx = (wp->width  - lineGap - marginW) / BOARD_WIDTH - lineGap;
2991         sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
2992         if(sqy < sqx) sqx = sqy;
2993         if(sqx != squareSize) {
2994             squareSize = sqx; // adopt new square size
2995             NewSurfaces();
2996             CreatePNGPieces(); // make newly scaled pieces
2997             InitDrawingSizes(0, 0); // creates grid etc.
2998         }
2999 }
3000
3001 static XtIntervalId delayedDragID = 0;
3002
3003 void
3004 DragProc ()
3005 {
3006         static int busy;
3007         if(busy) return;
3008
3009         busy = 1;
3010         GetActualPlacement(shellWidget, &wpNew);
3011         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3012            wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
3013             busy = 0; return; // false alarm
3014         }
3015         ReSize(&wpNew);
3016         if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
3017         if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3018         if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
3019         if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
3020         wpMain = wpNew;
3021         DrawPosition(True, NULL);
3022         delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3023         busy = 0;
3024 }
3025
3026
3027 void
3028 DelayedDrag ()
3029 {
3030     if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3031     delayedDragID =
3032       XtAppAddTimeOut(appContext, 100, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3033 }
3034
3035 void
3036 EventProc (Widget widget, caddr_t unused, XEvent *event)
3037 {
3038     if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
3039         DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3040 }
3041
3042 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
3043
3044 float
3045 Color (char *col, int n)
3046 {
3047   int c;
3048   sscanf(col, "#%x", &c);
3049   c = c >> 4*n & 255;
3050   return c/255.;
3051 }
3052
3053 void
3054 SetPen (cairo_t *cr, float w, char *col, int dash)
3055 {
3056   static const double dotted[] = {4.0, 4.0};
3057   static int len  = sizeof(dotted) / sizeof(dotted[0]);
3058   cairo_set_line_width (cr, w);
3059   cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
3060   if(dash) cairo_set_dash (cr, dotted, len, 0.0);
3061 }
3062
3063 void DrawSeekAxis( int x, int y, int xTo, int yTo )
3064 {
3065     cairo_t *cr;
3066
3067     /* get a cairo_t */
3068     cr = cairo_create (csBoardWindow);
3069
3070     cairo_move_to (cr, x, y);
3071     cairo_line_to(cr, xTo, yTo );
3072
3073     SetPen(cr, 2, "#000000", 0);
3074     cairo_stroke(cr);
3075
3076     /* free memory */
3077     cairo_destroy (cr);
3078 }
3079
3080 void DrawSeekBackground( int left, int top, int right, int bottom )
3081 {
3082     cairo_t *cr = cairo_create (csBoardWindow);
3083
3084     cairo_rectangle (cr, left, top, right-left, bottom-top);
3085
3086     cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
3087     cairo_fill(cr);
3088
3089     /* free memory */
3090     cairo_destroy (cr);
3091 }
3092
3093 void DrawSeekText(char *buf, int x, int y)
3094 {
3095     cairo_t *cr = cairo_create (csBoardWindow);
3096
3097     cairo_select_font_face (cr, "Sans",
3098                             CAIRO_FONT_SLANT_NORMAL,
3099                             CAIRO_FONT_WEIGHT_NORMAL);
3100
3101     cairo_set_font_size (cr, 12.0);
3102
3103     cairo_move_to (cr, x, y+4);
3104     cairo_set_source_rgba(cr, 0, 0, 0,1.0);
3105     cairo_show_text( cr, buf);
3106
3107     /* free memory */
3108     cairo_destroy (cr);
3109 }
3110
3111 void DrawSeekDot(int x, int y, int colorNr)
3112 {
3113     cairo_t *cr = cairo_create (csBoardWindow);
3114     int square = colorNr & 0x80;
3115     colorNr &= 0x7F;
3116
3117     if(square)
3118         cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3119     else
3120         cairo_arc(cr, x, y, squareSize/8, 0.0, 2*M_PI);
3121
3122     SetPen(cr, 2, "#000000", 0);
3123     cairo_stroke_preserve(cr);
3124     switch (colorNr) {
3125       case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
3126       case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
3127       default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
3128     }
3129     cairo_fill(cr);
3130
3131     /* free memory */
3132     cairo_destroy (cr);
3133 }
3134
3135 void
3136 DrawSeekOpen ()
3137 {
3138     int boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3139     int boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3140     if(!csBoardWindow) {
3141         csBoardWindow = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), boardWidth, boardHeight);
3142         csBoardBackup = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, boardWidth, boardHeight);
3143     }
3144 }
3145
3146 void
3147 DrawSeekClose ()
3148 {
3149 }
3150
3151 void
3152 DoDrawGrid(cairo_surface_t *cs)
3153 {
3154   /* draws a grid starting around Nx, Ny squares starting at x,y */
3155   int i;
3156   cairo_t *cr;
3157
3158   DrawSeekOpen();
3159   /* get a cairo_t */
3160   cr = cairo_create (cs);
3161
3162   cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
3163   SetPen(cr, lineGap, "#000000", 0);
3164
3165   /* lines in X */
3166   for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
3167     {
3168       cairo_move_to (cr, gridSegments[i].x1, gridSegments[i].y1);
3169       cairo_line_to (cr, gridSegments[i].x2, gridSegments[i].y2);
3170       cairo_stroke (cr);
3171     }
3172
3173   /* free memory */
3174   cairo_destroy (cr);
3175
3176   return;
3177 }
3178
3179 void
3180 DrawGrid()
3181 {
3182   DoDrawGrid(csBoardWindow);
3183   DoDrawGrid(csBoardBackup);
3184 }
3185
3186 /*
3187  * event handler for redrawing the board
3188  */
3189 void
3190 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3191 {
3192     DrawPosition(True, NULL);
3193 }
3194
3195
3196 void
3197 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3198 {   // [HGM] pv: walk PV
3199     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3200 }
3201
3202 static int savedIndex;  /* gross that this is global */
3203
3204 void
3205 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3206 {
3207         String val;
3208         XawTextPosition index, dummy;
3209         Arg arg;
3210
3211         XawTextGetSelectionPos(w, &index, &dummy);
3212         XtSetArg(arg, XtNstring, &val);
3213         XtGetValues(w, &arg, 1);
3214         ReplaceComment(savedIndex, val);
3215         if(savedIndex != currentMove) ToNrEvent(savedIndex);
3216         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3217 }
3218
3219 void
3220 EditCommentPopUp (int index, char *title, char *text)
3221 {
3222     savedIndex = index;
3223     if (text == NULL) text = "";
3224     NewCommentPopup(title, text, index);
3225 }
3226
3227 void
3228 CommentPopUp (char *title, char *text)
3229 {
3230     savedIndex = currentMove; // [HGM] vari
3231     NewCommentPopup(title, text, currentMove);
3232 }
3233
3234 void
3235 CommentPopDown ()
3236 {
3237     PopDown(CommentDlg);
3238 }
3239
3240
3241 /* Disable all user input other than deleting the window */
3242 static int frozen = 0;
3243
3244 void
3245 FreezeUI ()
3246 {
3247   if (frozen) return;
3248   /* Grab by a widget that doesn't accept input */
3249   XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
3250   frozen = 1;
3251 }
3252
3253 /* Undo a FreezeUI */
3254 void
3255 ThawUI ()
3256 {
3257   if (!frozen) return;
3258   XtRemoveGrab(optList[W_MESSG].handle);
3259   frozen = 0;
3260 }
3261
3262 void
3263 ModeHighlight ()
3264 {
3265     Arg args[16];
3266     static int oldPausing = FALSE;
3267     static GameMode oldmode = (GameMode) -1;
3268     char *wname;
3269
3270     if (!boardWidget || !XtIsRealized(boardWidget)) return;
3271
3272     if (pausing != oldPausing) {
3273         oldPausing = pausing;
3274         MarkMenuItem("Mode.Pause", pausing);
3275
3276         if (appData.showButtonBar) {
3277           /* Always toggle, don't set.  Previous code messes up when
3278              invoked while the button is pressed, as releasing it
3279              toggles the state again. */
3280           {
3281             Pixel oldbg, oldfg;
3282             XtSetArg(args[0], XtNbackground, &oldbg);
3283             XtSetArg(args[1], XtNforeground, &oldfg);
3284             XtGetValues(optList[W_PAUSE].handle,
3285                         args, 2);
3286             XtSetArg(args[0], XtNbackground, oldfg);
3287             XtSetArg(args[1], XtNforeground, oldbg);
3288           }
3289           XtSetValues(optList[W_PAUSE].handle, args, 2);
3290         }
3291     }
3292
3293     wname = ModeToWidgetName(oldmode);
3294     if (wname != NULL) {
3295         MarkMenuItem(wname, False);
3296     }
3297     wname = ModeToWidgetName(gameMode);
3298     if (wname != NULL) {
3299         MarkMenuItem(wname, True);
3300     }
3301     oldmode = gameMode;
3302     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
3303
3304     /* Maybe all the enables should be handled here, not just this one */
3305     EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
3306
3307     DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
3308 }
3309
3310
3311 /*
3312  * Button/menu procedures
3313  */
3314
3315 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3316 char *selected_fen_position=NULL;
3317
3318 Boolean
3319 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3320                        Atom *type_return, XtPointer *value_return,
3321                        unsigned long *length_return, int *format_return)
3322 {
3323   char *selection_tmp;
3324
3325 //  if (!selected_fen_position) return False; /* should never happen */
3326   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3327    if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3328     FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3329     long len;
3330     size_t count;
3331     if (f == NULL) return False;
3332     fseek(f, 0, 2);
3333     len = ftell(f);
3334     rewind(f);
3335     selection_tmp = XtMalloc(len + 1);
3336     count = fread(selection_tmp, 1, len, f);
3337     fclose(f);
3338     if (len != count) {
3339       XtFree(selection_tmp);
3340       return False;
3341     }
3342     selection_tmp[len] = NULLCHAR;
3343    } else {
3344     /* note: since no XtSelectionDoneProc was registered, Xt will
3345      * automatically call XtFree on the value returned.  So have to
3346      * make a copy of it allocated with XtMalloc */
3347     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3348     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3349    }
3350
3351     *value_return=selection_tmp;
3352     *length_return=strlen(selection_tmp);
3353     *type_return=*target;
3354     *format_return = 8; /* bits per byte */
3355     return True;
3356   } else if (*target == XA_TARGETS(xDisplay)) {
3357     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3358     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3359     targets_tmp[1] = XA_STRING;
3360     *value_return = targets_tmp;
3361     *type_return = XA_ATOM;
3362     *length_return = 2;
3363 #if 0
3364     // This code leads to a read of value_return out of bounds on 64-bit systems.
3365     // Other code which I have seen always sets *format_return to 32 independent of
3366     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3367     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3368     *format_return = 8 * sizeof(Atom);
3369     if (*format_return > 32) {
3370       *length_return *= *format_return / 32;
3371       *format_return = 32;
3372     }
3373 #else
3374     *format_return = 32;
3375 #endif
3376     return True;
3377   } else {
3378     return False;
3379   }
3380 }
3381
3382 /* note: when called from menu all parameters are NULL, so no clue what the
3383  * Widget which was clicked on was, or what the click event was
3384  */
3385 void
3386 CopySomething (char *src)
3387 {
3388     selected_fen_position = src;
3389     /*
3390      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3391      * have a notion of a position that is selected but not copied.
3392      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3393      */
3394     XtOwnSelection(menuBarWidget, XA_PRIMARY,
3395                    CurrentTime,
3396                    SendPositionSelection,
3397                    NULL/* lose_ownership_proc */ ,
3398                    NULL/* transfer_done_proc */);
3399     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3400                    CurrentTime,
3401                    SendPositionSelection,
3402                    NULL/* lose_ownership_proc */ ,
3403                    NULL/* transfer_done_proc */);
3404 }
3405
3406 /* function called when the data to Paste is ready */
3407 static void
3408 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3409                  Atom *type, XtPointer value, unsigned long *len, int *format)
3410 {
3411   char *fenstr=value;
3412   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3413   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3414   EditPositionPasteFEN(fenstr);
3415   XtFree(value);
3416 }
3417
3418 /* called when Paste Position button is pressed,
3419  * all parameters will be NULL */
3420 void
3421 PastePositionProc ()
3422 {
3423     XtGetSelectionValue(menuBarWidget,
3424       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3425       /* (XtSelectionCallbackProc) */ PastePositionCB,
3426       NULL, /* client_data passed to PastePositionCB */
3427
3428       /* better to use the time field from the event that triggered the
3429        * call to this function, but that isn't trivial to get
3430        */
3431       CurrentTime
3432     );
3433     return;
3434 }
3435
3436 /* note: when called from menu all parameters are NULL, so no clue what the
3437  * Widget which was clicked on was, or what the click event was
3438  */
3439 /* function called when the data to Paste is ready */
3440 static void
3441 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3442              Atom *type, XtPointer value, unsigned long *len, int *format)
3443 {
3444   FILE* f;
3445   if (value == NULL || *len == 0) {
3446     return; /* nothing had been selected to copy */
3447   }
3448   f = fopen(gamePasteFilename, "w");
3449   if (f == NULL) {
3450     DisplayError(_("Can't open temp file"), errno);
3451     return;
3452   }
3453   fwrite(value, 1, *len, f);
3454   fclose(f);
3455   XtFree(value);
3456   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3457 }
3458
3459 /* called when Paste Game button is pressed,
3460  * all parameters will be NULL */
3461 void
3462 PasteGameProc ()
3463 {
3464     XtGetSelectionValue(menuBarWidget,
3465       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3466       /* (XtSelectionCallbackProc) */ PasteGameCB,
3467       NULL, /* client_data passed to PasteGameCB */
3468
3469       /* better to use the time field from the event that triggered the
3470        * call to this function, but that isn't trivial to get
3471        */
3472       CurrentTime
3473     );
3474     return;
3475 }
3476
3477
3478 void
3479 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3480 {
3481     QuitProc();
3482 }
3483
3484 int
3485 ShiftKeys ()
3486 {   // bassic primitive for determining if modifier keys are pressed
3487     long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3488     char keys[32];
3489     int i,j,  k=0;
3490     XQueryKeymap(xDisplay,keys);
3491     for(i=0; i<6; i++) {
3492         k <<= 1;
3493         j = XKeysymToKeycode(xDisplay, codes[i]);
3494         k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3495     }
3496     return k;
3497 }
3498
3499 static void
3500 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3501 {
3502     char buf[10];
3503     KeySym sym;
3504     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3505     if ( n == 1 && *buf >= 32 // printable
3506          && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3507         ) BoxAutoPopUp (buf);
3508 }
3509
3510 static void
3511 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3512 {   // [HGM] input: let up-arrow recall previous line from history
3513     IcsKey(1);
3514 }
3515
3516 static void
3517 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3518 {   // [HGM] input: let down-arrow recall next line from history
3519     IcsKey(-1);
3520 }
3521
3522 static void
3523 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3524 {
3525     IcsKey(0);
3526 }
3527
3528 void
3529 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3530 {
3531         if (!TempBackwardActive) {
3532                 TempBackwardActive = True;
3533                 BackwardEvent();
3534         }
3535 }
3536
3537 void
3538 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3539 {
3540         /* Check to see if triggered by a key release event for a repeating key.
3541          * If so the next queued event will be a key press of the same key at the same time */
3542         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3543                 XEvent next;
3544                 XPeekEvent(xDisplay, &next);
3545                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3546                         next.xkey.keycode == event->xkey.keycode)
3547                                 return;
3548         }
3549     ForwardEvent();
3550         TempBackwardActive = False;
3551 }
3552
3553 void
3554 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3555 {   // called as key binding
3556     char buf[MSG_SIZ];
3557     String name;
3558     if (nprms && *nprms > 0)
3559       name = prms[0];
3560     else
3561       name = "xboard";
3562     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3563     system(buf);
3564 }
3565
3566 void
3567 ManProc ()
3568 {   // called from menu
3569     ManInner(NULL, NULL, NULL, NULL);
3570 }
3571
3572 void
3573 SetWindowTitle (char *text, char *title, char *icon)
3574 {
3575     Arg args[16];
3576     int i;
3577     if (appData.titleInWindow) {
3578         i = 0;
3579         XtSetArg(args[i], XtNlabel, text);   i++;
3580         XtSetValues(titleWidget, args, i);
3581     }
3582     i = 0;
3583     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
3584     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
3585     XtSetValues(shellWidget, args, i);
3586     XSync(xDisplay, False);
3587 }
3588
3589
3590 static int
3591 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3592 {
3593     return 0;
3594 }
3595
3596 void
3597 DisplayIcsInteractionTitle (String message)
3598 {
3599   if (oldICSInteractionTitle == NULL) {
3600     /* Magic to find the old window title, adapted from vim */
3601     char *wina = getenv("WINDOWID");
3602     if (wina != NULL) {
3603       Window win = (Window) atoi(wina);
3604       Window root, parent, *children;
3605       unsigned int nchildren;
3606       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3607       for (;;) {
3608         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3609         if (!XQueryTree(xDisplay, win, &root, &parent,
3610                         &children, &nchildren)) break;
3611         if (children) XFree((void *)children);
3612         if (parent == root || parent == 0) break;
3613         win = parent;
3614       }
3615       XSetErrorHandler(oldHandler);
3616     }
3617     if (oldICSInteractionTitle == NULL) {
3618       oldICSInteractionTitle = "xterm";
3619     }
3620   }
3621   printf("\033]0;%s\007", message);
3622   fflush(stdout);
3623 }
3624
3625
3626 XtIntervalId delayedEventTimerXID = 0;
3627 DelayedEventCallback delayedEventCallback = 0;
3628
3629 void
3630 FireDelayedEvent ()
3631 {
3632     delayedEventTimerXID = 0;
3633     delayedEventCallback();
3634 }
3635
3636 void
3637 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3638 {
3639     if(delayedEventTimerXID && delayedEventCallback == cb)
3640         // [HGM] alive: replace, rather than add or flush identical event
3641         XtRemoveTimeOut(delayedEventTimerXID);
3642     delayedEventCallback = cb;
3643     delayedEventTimerXID =
3644       XtAppAddTimeOut(appContext, millisec,
3645                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3646 }
3647
3648 DelayedEventCallback
3649 GetDelayedEvent ()
3650 {
3651   if (delayedEventTimerXID) {
3652     return delayedEventCallback;
3653   } else {
3654     return NULL;
3655   }
3656 }
3657
3658 void
3659 CancelDelayedEvent ()
3660 {
3661   if (delayedEventTimerXID) {
3662     XtRemoveTimeOut(delayedEventTimerXID);
3663     delayedEventTimerXID = 0;
3664   }
3665 }
3666
3667 XtIntervalId loadGameTimerXID = 0;
3668
3669 int
3670 LoadGameTimerRunning ()
3671 {
3672     return loadGameTimerXID != 0;
3673 }
3674
3675 int
3676 StopLoadGameTimer ()
3677 {
3678     if (loadGameTimerXID != 0) {
3679         XtRemoveTimeOut(loadGameTimerXID);
3680         loadGameTimerXID = 0;
3681         return TRUE;
3682     } else {
3683         return FALSE;
3684     }
3685 }
3686
3687 void
3688 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3689 {
3690     loadGameTimerXID = 0;
3691     AutoPlayGameLoop();
3692 }
3693
3694 void
3695 StartLoadGameTimer (long millisec)
3696 {
3697     loadGameTimerXID =
3698       XtAppAddTimeOut(appContext, millisec,
3699                       (XtTimerCallbackProc) LoadGameTimerCallback,
3700                       (XtPointer) 0);
3701 }
3702
3703 XtIntervalId analysisClockXID = 0;
3704
3705 void
3706 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3707 {
3708     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile