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