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