Make draw handle for board globally available
[xboard.git] / xboard.c
1 /*
2  * xboard.c -- X front end for XBoard
3  *
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,
5  * Massachusetts.
6  *
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8  * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
9  *
10  * The following terms apply to Digital Equipment Corporation's copyright
11  * interest in XBoard:
12  * ------------------------------------------------------------------------
13  * All Rights Reserved
14  *
15  * Permission to use, copy, modify, and distribute this software and its
16  * documentation for any purpose and without fee is hereby granted,
17  * provided that the above copyright notice appear in all copies and that
18  * both that copyright notice and this permission notice appear in
19  * supporting documentation, and that the name of Digital not be
20  * used in advertising or publicity pertaining to distribution of the
21  * software without specific, written prior permission.
22  *
23  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29  * SOFTWARE.
30  * ------------------------------------------------------------------------
31  *
32  * The following terms apply to the enhanced version of XBoard
33  * distributed by the Free Software Foundation:
34  * ------------------------------------------------------------------------
35  *
36  * GNU XBoard is free software: you can redistribute it and/or modify
37  * it under the terms of the GNU General Public License as published by
38  * the Free Software Foundation, either version 3 of the License, or (at
39  * your option) any later version.
40  *
41  * GNU XBoard is distributed in the hope that it will be useful, but
42  * WITHOUT ANY WARRANTY; without even the implied warranty of
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44  * General Public License for more details.
45  *
46  * You should have received a copy of the GNU General Public License
47  * along with this program. If not, see http://www.gnu.org/licenses/.  *
48  *
49  *------------------------------------------------------------------------
50  ** See the file ChangeLog for a revision history.  */
51
52 #define HIGHDRAG 1
53
54 #include "config.h"
55
56 #include <stdio.h>
57 #include <ctype.h>
58 #include <signal.h>
59 #include <errno.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <pwd.h>
63 #include <math.h>
64 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.h>
66
67 #if !OMIT_SOCKETS
68 # if HAVE_SYS_SOCKET_H
69 #  include <sys/socket.h>
70 #  include <netinet/in.h>
71 #  include <netdb.h>
72 # else /* not HAVE_SYS_SOCKET_H */
73 #  if HAVE_LAN_SOCKET_H
74 #   include <lan/socket.h>
75 #   include <lan/in.h>
76 #   include <lan/netdb.h>
77 #  else /* not HAVE_LAN_SOCKET_H */
78 #   define OMIT_SOCKETS 1
79 #  endif /* not HAVE_LAN_SOCKET_H */
80 # endif /* not HAVE_SYS_SOCKET_H */
81 #endif /* !OMIT_SOCKETS */
82
83 #if STDC_HEADERS
84 # include <stdlib.h>
85 # include <string.h>
86 #else /* not STDC_HEADERS */
87 extern char *getenv();
88 # if HAVE_STRING_H
89 #  include <string.h>
90 # else /* not HAVE_STRING_H */
91 #  include <strings.h>
92 # endif /* not HAVE_STRING_H */
93 #endif /* not STDC_HEADERS */
94
95 #if HAVE_SYS_FCNTL_H
96 # include <sys/fcntl.h>
97 #else /* not HAVE_SYS_FCNTL_H */
98 # if HAVE_FCNTL_H
99 #  include <fcntl.h>
100 # endif /* HAVE_FCNTL_H */
101 #endif /* not HAVE_SYS_FCNTL_H */
102
103 #if HAVE_SYS_SYSTEMINFO_H
104 # include <sys/systeminfo.h>
105 #endif /* HAVE_SYS_SYSTEMINFO_H */
106
107 #if TIME_WITH_SYS_TIME
108 # include <sys/time.h>
109 # include <time.h>
110 #else
111 # if HAVE_SYS_TIME_H
112 #  include <sys/time.h>
113 # else
114 #  include <time.h>
115 # endif
116 #endif
117
118 #if HAVE_UNISTD_H
119 # include <unistd.h>
120 #endif
121
122 #if HAVE_SYS_WAIT_H
123 # include <sys/wait.h>
124 #endif
125
126 #if HAVE_DIRENT_H
127 # include <dirent.h>
128 # define NAMLEN(dirent) strlen((dirent)->d_name)
129 # define HAVE_DIR_STRUCT
130 #else
131 # define dirent direct
132 # define NAMLEN(dirent) (dirent)->d_namlen
133 # if HAVE_SYS_NDIR_H
134 #  include <sys/ndir.h>
135 #  define HAVE_DIR_STRUCT
136 # endif
137 # if HAVE_SYS_DIR_H
138 #  include <sys/dir.h>
139 #  define HAVE_DIR_STRUCT
140 # endif
141 # if HAVE_NDIR_H
142 #  include <ndir.h>
143 #  define HAVE_DIR_STRUCT
144 # endif
145 #endif
146
147 #if ENABLE_NLS
148 #include <locale.h>
149 #endif
150
151 #include <X11/Intrinsic.h>
152 #include <X11/StringDefs.h>
153 #include <X11/Shell.h>
154 #include <X11/cursorfont.h>
155 #include <X11/Xatom.h>
156 #include <X11/Xmu/Atoms.h>
157 #if USE_XAW3D
158 #include <X11/Xaw3d/Dialog.h>
159 #include <X11/Xaw3d/Form.h>
160 #include <X11/Xaw3d/List.h>
161 #include <X11/Xaw3d/Label.h>
162 #include <X11/Xaw3d/SimpleMenu.h>
163 #include <X11/Xaw3d/SmeBSB.h>
164 #include <X11/Xaw3d/SmeLine.h>
165 #include <X11/Xaw3d/Box.h>
166 #include <X11/Xaw3d/MenuButton.h>
167 #include <X11/Xaw3d/Text.h>
168 #include <X11/Xaw3d/AsciiText.h>
169 #else
170 #include <X11/Xaw/Dialog.h>
171 #include <X11/Xaw/Form.h>
172 #include <X11/Xaw/List.h>
173 #include <X11/Xaw/Label.h>
174 #include <X11/Xaw/SimpleMenu.h>
175 #include <X11/Xaw/SmeBSB.h>
176 #include <X11/Xaw/SmeLine.h>
177 #include <X11/Xaw/Box.h>
178 #include <X11/Xaw/MenuButton.h>
179 #include <X11/Xaw/Text.h>
180 #include <X11/Xaw/AsciiText.h>
181 #endif
182
183 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
184 #include "common.h"
185
186 #if HAVE_LIBXPM
187 #include <X11/xpm.h>
188 #define IMAGE_EXT "xpm"
189 #else
190 #define IMAGE_EXT "xim"
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 "xevalgraph.h"
206 #include "xedittags.h"
207 #include "menus.h"
208 #include "board.h"
209 #include "dialogs.h"
210 #include "engineoutput.h"
211 #include "usystem.h"
212 #include "gettext.h"
213 #include "draw.h"
214
215
216 #ifdef __EMX__
217 #ifndef HAVE_USLEEP
218 #define HAVE_USLEEP
219 #endif
220 #define usleep(t)   _sleep2(((t)+500)/1000)
221 #endif
222
223 #ifdef ENABLE_NLS
224 # define  _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
226 #else
227 # define  _(s) (s)
228 # define N_(s)  s
229 #endif
230
231 int main P((int argc, char **argv));
232 RETSIGTYPE CmailSigHandler P((int sig));
233 RETSIGTYPE IntSigHandler P((int sig));
234 RETSIGTYPE TermSizeSigHandler P((int sig));
235 Widget CreateMenuBar P((Menu *mb, int boardWidth));
236 #if ENABLE_NLS
237 char *InsertPxlSize P((char *pattern, int targetPxlSize));
238 XFontSet CreateFontSet P((char *base_fnt_lst));
239 #else
240 char *FindFont P((char *pattern, int targetPxlSize));
241 #endif
242 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
243                    u_int wreq, u_int hreq));
244 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
245 void DelayedDrag P((void));
246 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
247 void HandlePV P((Widget w, XEvent * event,
248                      String * params, Cardinal * nParams));
249 void DrawPositionProc P((Widget w, XEvent *event,
250                      String *prms, Cardinal *nprms));
251 void CommentClick P((Widget w, XEvent * event,
252                    String * params, Cardinal * nParams));
253 void ICSInputBoxPopUp P((void));
254 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
255 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
256 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
257 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
258 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
259 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
260 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
261 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
262 Boolean TempBackwardActive = False;
263 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
264 void DisplayMove P((int moveNumber));
265 void ICSInitScript P((void));
266 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
267 void update_ics_width P(());
268 int CopyMemoProc P(());
269
270 /*
271 * XBoard depends on Xt R4 or higher
272 */
273 int xtVersion = XtSpecificationRelease;
274
275 int xScreen;
276 Display *xDisplay;
277 Window xBoardWindow;
278 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
279   highlightSquareColor, premoveHighlightColor;       // used in graphics
280 Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
281 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
282 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
283 Option *optList; // contains all widgets of main window
284 #if ENABLE_NLS
285 XFontSet fontSet, clockFontSet;
286 #else
287 Font clockFontID;
288 XFontStruct *clockFontStruct;
289 #endif
290 Font coordFontID, countFontID;
291 XFontStruct *coordFontStruct, *countFontStruct;
292 XtAppContext appContext;
293 char *layoutName;
294
295 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
296
297 Position commentX = -1, commentY = -1;
298 Dimension commentW, commentH;
299 typedef unsigned int BoardSize;
300 BoardSize boardSize;
301 Boolean chessProgram;
302
303 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
304 int smallLayout = 0, tinyLayout = 0,
305   marginW, marginH, // [HGM] for run-time resizing
306   fromX = -1, fromY = -1, toX, toY, commentUp = False,
307   errorExitStatus = -1, defaultLineGap;
308 Dimension textHeight;
309 Pixel timerForegroundPixel, timerBackgroundPixel;
310 Pixel buttonForegroundPixel, buttonBackgroundPixel;
311 char *chessDir, *programName, *programVersion;
312 Boolean alwaysOnTop = False;
313 char *icsTextMenuString;
314 char *icsNames;
315 char *firstChessProgramNames;
316 char *secondChessProgramNames;
317
318 WindowPlacement wpMain;
319 WindowPlacement wpConsole;
320 WindowPlacement wpComment;
321 WindowPlacement wpMoveHistory;
322 WindowPlacement wpEvalGraph;
323 WindowPlacement wpEngineOutput;
324 WindowPlacement wpGameList;
325 WindowPlacement wpTags;
326
327
328 /* This magic number is the number of intermediate frames used
329    in each half of the animation. For short moves it's reduced
330    by 1. The total number of frames will be factor * 2 + 1.  */
331 #define kFactor    4
332
333 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
334
335 typedef struct {
336     char piece;
337     char* widget;
338 } DropMenuEnables;
339
340 DropMenuEnables dmEnables[] = {
341     { 'P', "Pawn" },
342     { 'N', "Knight" },
343     { 'B', "Bishop" },
344     { 'R', "Rook" },
345     { 'Q', "Queen" }
346 };
347
348 Arg shellArgs[] = {
349     { XtNwidth, 0 },
350     { XtNheight, 0 },
351     { XtNminWidth, 0 },
352     { XtNminHeight, 0 },
353     { XtNmaxWidth, 0 },
354     { XtNmaxHeight, 0 }
355 };
356
357 XtResource clientResources[] = {
358     { "flashCount", "flashCount", XtRInt, sizeof(int),
359         XtOffset(AppDataPtr, flashCount), XtRImmediate,
360         (XtPointer) FLASH_COUNT  },
361 };
362
363 XrmOptionDescRec shellOptions[] = {
364     { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
365     { "-flash", "flashCount", XrmoptionNoArg, "3" },
366     { "-xflash", "flashCount", XrmoptionNoArg, "0" },
367 };
368
369 XtActionsRec boardActions[] = {
370     { "DrawPosition", DrawPositionProc },
371     { "HandlePV", HandlePV },
372     { "SelectPV", SelectPV },
373     { "StopPV", StopPV },
374     { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
375     { "QuitProc", QuitWrapper },
376     { "ManProc", ManInner },
377     { "TempBackwardProc", TempBackwardProc },
378     { "TempForwardProc", TempForwardProc },
379     { "CommentClick", (XtActionProc) CommentClick },
380     { "GenericPopDown", (XtActionProc) GenericPopDown },
381     { "ErrorPopDown", (XtActionProc) ErrorPopDown },
382     { "CopyMemoProc", (XtActionProc) CopyMemoProc },
383     { "SelectMove", (XtActionProc) SelectMove },
384     { "LoadSelectedProc", LoadSelectedProc },
385     { "SetFilterProc", SetFilterProc },
386     { "TypeInProc", TypeInProc },
387     { "EnterKeyProc", EnterKeyProc },
388     { "UpKeyProc", UpKeyProc },
389     { "DownKeyProc", DownKeyProc },
390     { "WheelProc", WheelProc },
391     { "TabProc", TabProc },
392 };
393
394 char globalTranslations[] =
395   ":<Key>F9: MenuItem(Actions.Resign) \n \
396    :Ctrl<Key>n: MenuItem(File.NewGame) \n \
397    :Meta<Key>V: MenuItem(File.NewVariant) \n \
398    :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
399    :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
400    :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
401    :Ctrl<Key>Down: LoadSelectedProc(3) \n \
402    :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
403    :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
404    :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
405    :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
406    :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
407    :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
408    :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
409    :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
410    :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
411    :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
412    :Ctrl<Key>q: MenuItem(File.Quit) \n \
413    :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
414    :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
415    :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
416    :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
417    :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
418    :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
419    :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
420    :Meta<Key>O: MenuItem(View.EngineOutput) \n \
421    :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
422    :Meta<Key>G: MenuItem(View.GameList) \n \
423    :Meta<Key>H: MenuItem(View.MoveHistory) \n \
424    :<Key>Pause: MenuItem(Mode.Pause) \n \
425    :<Key>F3: MenuItem(Action.Accept) \n \
426    :<Key>F4: MenuItem(Action.Decline) \n \
427    :<Key>F12: MenuItem(Action.Rematch) \n \
428    :<Key>F5: MenuItem(Action.CallFlag) \n \
429    :<Key>F6: MenuItem(Action.Draw) \n \
430    :<Key>F7: MenuItem(Action.Adjourn) \n \
431    :<Key>F8: MenuItem(Action.Abort) \n \
432    :<Key>F10: MenuItem(Action.StopObserving) \n \
433    :<Key>F11: MenuItem(Action.StopExamining) \n \
434    :Ctrl<Key>d: MenuItem(DebugProc) \n \
435    :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
436    :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
437    :Meta<Key>Right: MenuItem(Edit.Forward) \n \
438    :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
439    :Meta<Key>Left: MenuItem(Edit.Backward) \n \
440    :<Key>Left: MenuItem(Edit.Backward) \n \
441    :<Key>Right: MenuItem(Edit.Forward) \n \
442    :<Key>Home: MenuItem(Edit.Revert) \n \
443    :<Key>End: MenuItem(Edit.TruncateGame) \n \
444    :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
445    :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
446    :Meta<Key>J: MenuItem(Options.Adjudications) \n \
447    :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
448    :Meta<Key>T: MenuItem(Options.TimeControl) \n \
449    :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
450 #ifndef OPTIONSDIALOG
451     "\
452    :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
453    :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
454    :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
455    :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
456    :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
457 #endif
458    "\
459    :<Key>F1: MenuItem(Help.ManXBoard) \n \
460    :<Key>F2: MenuItem(View.FlipView) \n \
461    :<KeyDown>Return: TempBackwardProc() \n \
462    :<KeyUp>Return: TempForwardProc() \n";
463
464 char ICSInputTranslations[] =
465     "<Key>Up: UpKeyProc() \n "
466     "<Key>Down: DownKeyProc() \n "
467     "<Key>Return: EnterKeyProc() \n";
468
469 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
470 //             as the widget is destroyed before the up-click can call extend-end
471 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
472
473 String xboardResources[] = {
474     "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
475     NULL
476   };
477
478
479 /* Max possible square size */
480 #define MAXSQSIZE 256
481
482 static int xpm_avail[MAXSQSIZE];
483
484 #ifdef HAVE_DIR_STRUCT
485
486 /* Extract piece size from filename */
487 static int
488 xpm_getsize (char *name, int len, char *ext)
489 {
490     char *p, *d;
491     char buf[10];
492
493     if (len < 4)
494       return 0;
495
496     if ((p=strchr(name, '.')) == NULL ||
497         StrCaseCmp(p+1, ext) != 0)
498       return 0;
499
500     p = name + 3;
501     d = buf;
502
503     while (*p && isdigit(*p))
504       *(d++) = *(p++);
505
506     *d = 0;
507     return atoi(buf);
508 }
509
510 /* Setup xpm_avail */
511 static int
512 xpm_getavail (char *dirname, char *ext)
513 {
514     DIR *dir;
515     struct dirent *ent;
516     int  i;
517
518     for (i=0; i<MAXSQSIZE; ++i)
519       xpm_avail[i] = 0;
520
521     if (appData.debugMode)
522       fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
523
524     dir = opendir(dirname);
525     if (!dir)
526       {
527           fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
528                   programName, dirname);
529           exit(1);
530       }
531
532     while ((ent=readdir(dir)) != NULL) {
533         i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
534         if (i > 0 && i < MAXSQSIZE)
535           xpm_avail[i] = 1;
536     }
537
538     closedir(dir);
539
540     return 0;
541 }
542
543 void
544 xpm_print_avail (FILE *fp, char *ext)
545 {
546     int i;
547
548     fprintf(fp, _("Available `%s' sizes:\n"), ext);
549     for (i=1; i<MAXSQSIZE; ++i) {
550         if (xpm_avail[i])
551           printf("%d\n", i);
552     }
553 }
554
555 /* Return XPM piecesize closest to size */
556 int
557 xpm_closest_to (char *dirname, int size, char *ext)
558 {
559     int i;
560     int sm_diff = MAXSQSIZE;
561     int sm_index = 0;
562     int diff;
563
564     xpm_getavail(dirname, ext);
565
566     if (appData.debugMode)
567       xpm_print_avail(stderr, ext);
568
569     for (i=1; i<MAXSQSIZE; ++i) {
570         if (xpm_avail[i]) {
571             diff = size - i;
572             diff = (diff<0) ? -diff : diff;
573             if (diff < sm_diff) {
574                 sm_diff = diff;
575                 sm_index = i;
576             }
577         }
578     }
579
580     if (!sm_index) {
581         fprintf(stderr, _("Error: No `%s' files!\n"), ext);
582         exit(1);
583     }
584
585     return sm_index;
586 }
587 #else   /* !HAVE_DIR_STRUCT */
588 /* If we are on a system without a DIR struct, we can't
589    read the directory, so we can't collect a list of
590    filenames, etc., so we can't do any size-fitting. */
591 int
592 xpm_closest_to (char *dirname, int size, char *ext)
593 {
594     fprintf(stderr, _("\
595 Warning: No DIR structure found on this system --\n\
596          Unable to autosize for XPM/XIM pieces.\n\
597    Please report this error to %s.\n\
598    Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
599     return size;
600 }
601 #endif /* HAVE_DIR_STRUCT */
602
603
604 /* Arrange to catch delete-window events */
605 Atom wm_delete_window;
606 void
607 CatchDeleteWindow (Widget w, String procname)
608 {
609   char buf[MSG_SIZ];
610   XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
611   snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
612   XtAugmentTranslations(w, XtParseTranslationTable(buf));
613 }
614
615 void
616 BoardToTop ()
617 {
618   Arg args[16];
619   XtSetArg(args[0], XtNiconic, False);
620   XtSetValues(shellWidget, args, 1);
621
622   XtPopup(shellWidget, XtGrabNone); /* Raise if lowered  */
623 }
624
625 //---------------------------------------------------------------------------------------------------------
626 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
627 #define XBOARD True
628 #define JAWS_ARGS
629 #define CW_USEDEFAULT (1<<31)
630 #define ICS_TEXT_MENU_SIZE 90
631 #define DEBUG_FILE "xboard.debug"
632 #define SetCurrentDirectory chdir
633 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
634 #define OPTCHAR "-"
635 #define SEPCHAR " "
636
637 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
638 #include "args.h"
639
640 // front-end part of option handling
641
642 // [HGM] This platform-dependent table provides the location for storing the color info
643 extern char *crWhite, * crBlack;
644
645 void *
646 colorVariable[] = {
647   &appData.whitePieceColor,
648   &appData.blackPieceColor,
649   &appData.lightSquareColor,
650   &appData.darkSquareColor,
651   &appData.highlightSquareColor,
652   &appData.premoveHighlightColor,
653   &appData.lowTimeWarningColor,
654   NULL,
655   NULL,
656   NULL,
657   NULL,
658   NULL,
659   &crWhite,
660   &crBlack,
661   NULL
662 };
663
664 // [HGM] font: keep a font for each square size, even non-stndard ones
665 #define NUM_SIZES 18
666 #define MAX_SIZE 130
667 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
668 char *fontTable[NUM_FONTS][MAX_SIZE];
669
670 void
671 ParseFont (char *name, int number)
672 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
673   int size;
674   if(sscanf(name, "size%d:", &size)) {
675     // [HGM] font: font is meant for specific boardSize (likely from settings file);
676     //       defer processing it until we know if it matches our board size
677     if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
678         fontTable[number][size] = strdup(strchr(name, ':')+1);
679         fontValid[number][size] = True;
680     }
681     return;
682   }
683   switch(number) {
684     case 0: // CLOCK_FONT
685         appData.clockFont = strdup(name);
686       break;
687     case 1: // MESSAGE_FONT
688         appData.font = strdup(name);
689       break;
690     case 2: // COORD_FONT
691         appData.coordFont = strdup(name);
692       break;
693     default:
694       return;
695   }
696   fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
697 }
698
699 void
700 SetFontDefaults ()
701 { // only 2 fonts currently
702   appData.clockFont = CLOCK_FONT_NAME;
703   appData.coordFont = COORD_FONT_NAME;
704   appData.font  =   DEFAULT_FONT_NAME;
705 }
706
707 void
708 CreateFonts ()
709 { // no-op, until we identify the code for this already in XBoard and move it here
710 }
711
712 void
713 ParseColor (int n, char *name)
714 { // in XBoard, just copy the color-name string
715   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
716 }
717
718 void
719 ParseTextAttribs (ColorClass cc, char *s)
720 {
721     (&appData.colorShout)[cc] = strdup(s);
722 }
723
724 void
725 ParseBoardSize (void *addr, char *name)
726 {
727     appData.boardSize = strdup(name);
728 }
729
730 void
731 LoadAllSounds ()
732 { // In XBoard the sound-playing program takes care of obtaining the actual sound
733 }
734
735 void
736 SetCommPortDefaults ()
737 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
738 }
739
740 // [HGM] args: these three cases taken out to stay in front-end
741 void
742 SaveFontArg (FILE *f, ArgDescriptor *ad)
743 {
744   char *name;
745   int i, n = (int)(intptr_t)ad->argLoc;
746   switch(n) {
747     case 0: // CLOCK_FONT
748         name = appData.clockFont;
749       break;
750     case 1: // MESSAGE_FONT
751         name = appData.font;
752       break;
753     case 2: // COORD_FONT
754         name = appData.coordFont;
755       break;
756     default:
757       return;
758   }
759   for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
760     if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
761         fontTable[n][squareSize] = strdup(name);
762         fontValid[n][squareSize] = True;
763         break;
764   }
765   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
766     fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
767 }
768
769 void
770 ExportSounds ()
771 { // nothing to do, as the sounds are at all times represented by their text-string names already
772 }
773
774 void
775 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
776 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
777         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
778 }
779
780 void
781 SaveColor (FILE *f, ArgDescriptor *ad)
782 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
783         if(colorVariable[(int)(intptr_t)ad->argLoc])
784         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
785 }
786
787 void
788 SaveBoardSize (FILE *f, char *name, void *addr)
789 { // wrapper to shield back-end from BoardSize & sizeInfo
790   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
791 }
792
793 void
794 ParseCommPortSettings (char *s)
795 { // no such option in XBoard (yet)
796 }
797
798 int frameX, frameY;
799
800 void
801 GetActualPlacement (Widget wg, WindowPlacement *wp)
802 {
803   XWindowAttributes winAt;
804   Window win, dummy;
805   int rx, ry;
806
807   if(!wg) return;
808
809   win = XtWindow(wg);
810   XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
811   XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
812   wp->x = rx - winAt.x;
813   wp->y = ry - winAt.y;
814   wp->height = winAt.height;
815   wp->width = winAt.width;
816   frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
817 }
818
819 void
820 GetWindowCoords ()
821 { // wrapper to shield use of window handles from back-end (make addressible by number?)
822   // In XBoard this will have to wait until awareness of window parameters is implemented
823   GetActualPlacement(shellWidget, &wpMain);
824   if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
825   if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
826   if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
827   if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
828   if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
829   if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
830 }
831
832 void
833 PrintCommPortSettings (FILE *f, char *name)
834 { // This option does not exist in XBoard
835 }
836
837 void
838 EnsureOnScreen (int *x, int *y, int minX, int minY)
839 {
840   return;
841 }
842
843 int
844 MainWindowUp ()
845 { // [HGM] args: allows testing if main window is realized from back-end
846   return xBoardWindow != 0;
847 }
848
849 void
850 PopUpStartupDialog ()
851 {  // start menu not implemented in XBoard
852 }
853
854 char *
855 ConvertToLine (int argc, char **argv)
856 {
857   static char line[128*1024], buf[1024];
858   int i;
859
860   line[0] = NULLCHAR;
861   for(i=1; i<argc; i++)
862     {
863       if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
864           && argv[i][0] != '{' )
865         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
866       else
867         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
868       strncat(line, buf, 128*1024 - strlen(line) - 1 );
869     }
870
871   line[strlen(line)-1] = NULLCHAR;
872   return line;
873 }
874
875 //--------------------------------------------------------------------------------------------
876
877 void
878 ResizeBoardWindow (int w, int h, int inhibit)
879 {
880     w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
881     h += marginH;
882     shellArgs[0].value = w;
883     shellArgs[1].value = h;
884     shellArgs[4].value = shellArgs[2].value = w;
885     shellArgs[5].value = shellArgs[3].value = h;
886     XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
887
888     XSync(xDisplay, False);
889 }
890
891 static int
892 MakeOneColor (char *name, Pixel *color)
893 {
894     XrmValue vFrom, vTo;
895     if (!appData.monoMode) {
896         vFrom.addr = (caddr_t) name;
897         vFrom.size = strlen(name);
898         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
899         if (vTo.addr == NULL) {
900           appData.monoMode = True;
901           return True;
902         } else {
903           *color = *(Pixel *) vTo.addr;
904         }
905     }
906     return False;
907 }
908
909 int
910 MakeColors ()
911 {   // [HGM] taken out of main(), so it can be called from BoardOptions dialog
912     int forceMono = False;
913
914     forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
915     forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
916     forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
917     forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
918     forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
919     forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
920     if (appData.lowTimeWarning)
921         forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
922     if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
923     if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
924
925     return forceMono;
926 }
927
928 void
929 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
930 {   // detervtomine what fonts to use, and create them
931     XrmValue vTo;
932     XrmDatabase xdb;
933
934     if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
935         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
936     if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
937         appData.font = fontTable[MESSAGE_FONT][squareSize];
938     if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
939         appData.coordFont = fontTable[COORD_FONT][squareSize];
940
941 #if ENABLE_NLS
942     appData.font = InsertPxlSize(appData.font, fontPxlSize);
943     appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
944     appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
945     fontSet = CreateFontSet(appData.font);
946     clockFontSet = CreateFontSet(appData.clockFont);
947     {
948       /* For the coordFont, use the 0th font of the fontset. */
949       XFontSet coordFontSet = CreateFontSet(appData.coordFont);
950       XFontStruct **font_struct_list;
951       XFontSetExtents *fontSize;
952       char **font_name_list;
953       XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
954       coordFontID = XLoadFont(xDisplay, font_name_list[0]);
955       coordFontStruct = XQueryFont(xDisplay, coordFontID);
956       fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
957       textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
958     }
959 #else
960     appData.font = FindFont(appData.font, fontPxlSize);
961     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
962     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
963     clockFontID = XLoadFont(xDisplay, appData.clockFont);
964     clockFontStruct = XQueryFont(xDisplay, clockFontID);
965     coordFontID = XLoadFont(xDisplay, appData.coordFont);
966     coordFontStruct = XQueryFont(xDisplay, coordFontID);
967     // textHeight in !NLS mode!
968 #endif
969     countFontID = coordFontID;  // [HGM] holdings
970     countFontStruct = coordFontStruct;
971
972     xdb = XtDatabase(xDisplay);
973 #if ENABLE_NLS
974     XrmPutLineResource(&xdb, "*international: True");
975     vTo.size = sizeof(XFontSet);
976     vTo.addr = (XtPointer) &fontSet;
977     XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
978 #else
979     XrmPutStringResource(&xdb, "*font", appData.font);
980 #endif
981 }
982
983 char *
984 PrintArg (ArgType t)
985 {
986   char *p="";
987   switch(t) {
988     case ArgZ:
989     case ArgInt:      p = " N"; break;
990     case ArgString:   p = " STR"; break;
991     case ArgBoolean:  p = " TF"; break;
992     case ArgSettingsFilename:
993     case ArgFilename: p = " FILE"; break;
994     case ArgX:        p = " Nx"; break;
995     case ArgY:        p = " Ny"; break;
996     case ArgAttribs:  p = " TEXTCOL"; break;
997     case ArgColor:    p = " COL"; break;
998     case ArgFont:     p = " FONT"; break;
999     case ArgBoardSize: p = " SIZE"; break;
1000     case ArgFloat: p = " FLOAT"; break;
1001     case ArgTrue:
1002     case ArgFalse:
1003     case ArgTwo:
1004     case ArgNone:
1005     case ArgCommSettings:
1006       break;
1007   }
1008   return p;
1009 }
1010
1011 void
1012 PrintOptions ()
1013 {
1014   char buf[MSG_SIZ];
1015   int len=0;
1016   ArgDescriptor *q, *p = argDescriptors+5;
1017   printf("\nXBoard accepts the following options:\n"
1018          "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1019          " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1020          " SIZE = board-size spec(s)\n"
1021          " Within parentheses are short forms, or options to set to true or false.\n"
1022          " Persistent options (saved in the settings file) are marked with *)\n\n");
1023   while(p->argName) {
1024     if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1025     snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1026     if(p->save) strcat(buf+len, "*");
1027     for(q=p+1; q->argLoc == p->argLoc; q++) {
1028       if(q->argName[0] == '-') continue;
1029       strcat(buf+len, q == p+1 ? " (" : " ");
1030       sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1031     }
1032     if(q != p+1) strcat(buf+len, ")");
1033     len = strlen(buf);
1034     if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1035     p = q;
1036   }
1037   if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1038 }
1039
1040 int
1041 main (int argc, char **argv)
1042 {
1043     int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1044     XSetWindowAttributes window_attributes;
1045     Arg args[16];
1046     Dimension boardWidth, boardHeight, w, h;
1047     char *p;
1048     int forceMono = False;
1049
1050     srandom(time(0)); // [HGM] book: make random truly random
1051
1052     setbuf(stdout, NULL);
1053     setbuf(stderr, NULL);
1054     debugFP = stderr;
1055
1056     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1057         printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1058         exit(0);
1059     }
1060
1061     if(argc > 1 && !strcmp(argv[1], "--help" )) {
1062         PrintOptions();
1063         exit(0);
1064     }
1065
1066     programName = strrchr(argv[0], '/');
1067     if (programName == NULL)
1068       programName = argv[0];
1069     else
1070       programName++;
1071
1072 #ifdef ENABLE_NLS
1073     XtSetLanguageProc(NULL, NULL, NULL);
1074     if (appData.debugMode) {
1075       fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1076     }
1077
1078     bindtextdomain(PACKAGE, LOCALEDIR);
1079     textdomain(PACKAGE);
1080 #endif
1081
1082     appData.boardSize = "";
1083     InitAppData(ConvertToLine(argc, argv));
1084     p = getenv("HOME");
1085     if (p == NULL) p = "/tmp";
1086     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1087     gameCopyFilename = (char*) malloc(i);
1088     gamePasteFilename = (char*) malloc(i);
1089     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1090     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1091
1092     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1093         static char buf[MSG_SIZ];
1094         EscapeExpand(buf, appData.firstInitString);
1095         appData.firstInitString = strdup(buf);
1096         EscapeExpand(buf, appData.secondInitString);
1097         appData.secondInitString = strdup(buf);
1098         EscapeExpand(buf, appData.firstComputerString);
1099         appData.firstComputerString = strdup(buf);
1100         EscapeExpand(buf, appData.secondComputerString);
1101         appData.secondComputerString = strdup(buf);
1102     }
1103
1104     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1105         chessDir = ".";
1106     } else {
1107         if (chdir(chessDir) != 0) {
1108             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1109             perror(chessDir);
1110             exit(1);
1111         }
1112     }
1113
1114     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1115         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1116         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
1117            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1118            exit(errno);
1119         }
1120         setbuf(debugFP, NULL);
1121     }
1122
1123     /* [HGM,HR] make sure board size is acceptable */
1124     if(appData.NrFiles > BOARD_FILES ||
1125        appData.NrRanks > BOARD_RANKS   )
1126          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1127
1128 #if !HIGHDRAG
1129     /* This feature does not work; animation needs a rewrite */
1130     appData.highlightDragging = FALSE;
1131 #endif
1132     InitBackEnd1();
1133
1134         gameInfo.variant = StringToVariant(appData.variant);
1135         InitPosition(FALSE);
1136
1137     shellWidget =
1138       XtAppInitialize(&appContext, "XBoard", shellOptions,
1139                       XtNumber(shellOptions),
1140                       &argc, argv, xboardResources, NULL, 0);
1141
1142     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1143                               clientResources, XtNumber(clientResources),
1144                               NULL, 0);
1145
1146     xDisplay = XtDisplay(shellWidget);
1147     xScreen = DefaultScreen(xDisplay);
1148     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1149
1150     /*
1151      * determine size, based on supplied or remembered -size, or screen size
1152      */
1153     if (isdigit(appData.boardSize[0])) {
1154         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1155                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1156                    &fontPxlSize, &smallLayout, &tinyLayout);
1157         if (i == 0) {
1158             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1159                     programName, appData.boardSize);
1160             exit(2);
1161         }
1162         if (i < 7) {
1163             /* Find some defaults; use the nearest known size */
1164             SizeDefaults *szd, *nearest;
1165             int distance = 99999;
1166             nearest = szd = sizeDefaults;
1167             while (szd->name != NULL) {
1168                 if (abs(szd->squareSize - squareSize) < distance) {
1169                     nearest = szd;
1170                     distance = abs(szd->squareSize - squareSize);
1171                     if (distance == 0) break;
1172                 }
1173                 szd++;
1174             }
1175             if (i < 2) lineGap = nearest->lineGap;
1176             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1177             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1178             if (i < 5) fontPxlSize = nearest->fontPxlSize;
1179             if (i < 6) smallLayout = nearest->smallLayout;
1180             if (i < 7) tinyLayout = nearest->tinyLayout;
1181         }
1182     } else {
1183         SizeDefaults *szd = sizeDefaults;
1184         if (*appData.boardSize == NULLCHAR) {
1185             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1186                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1187               szd++;
1188             }
1189             if (szd->name == NULL) szd--;
1190             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1191         } else {
1192             while (szd->name != NULL &&
1193                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1194             if (szd->name == NULL) {
1195                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1196                         programName, appData.boardSize);
1197                 exit(2);
1198             }
1199         }
1200         squareSize = szd->squareSize;
1201         lineGap = szd->lineGap;
1202         clockFontPxlSize = szd->clockFontPxlSize;
1203         coordFontPxlSize = szd->coordFontPxlSize;
1204         fontPxlSize = szd->fontPxlSize;
1205         smallLayout = szd->smallLayout;
1206         tinyLayout = szd->tinyLayout;
1207         // [HGM] font: use defaults from settings file if available and not overruled
1208     }
1209
1210     defaultLineGap = lineGap;
1211     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1212
1213     /* [HR] height treated separately (hacked) */
1214     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1215     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1216
1217     /*
1218      * Determine what fonts to use.
1219      */
1220     InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1221
1222     /*
1223      * Detect if there are not enough colors available and adapt.
1224      */
1225     if (DefaultDepth(xDisplay, xScreen) <= 2) {
1226       appData.monoMode = True;
1227     }
1228
1229     forceMono = MakeColors();
1230
1231     if (forceMono) {
1232       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1233               programName);
1234         appData.monoMode = True;
1235     }
1236
1237     if (appData.monoMode && appData.debugMode) {
1238         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1239                 (unsigned long) XWhitePixel(xDisplay, xScreen),
1240                 (unsigned long) XBlackPixel(xDisplay, xScreen));
1241     }
1242
1243     ParseIcsTextColors();
1244
1245     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1246
1247     /*
1248      * widget hierarchy
1249      */
1250     if (tinyLayout) {
1251         layoutName = "tinyLayout";
1252     } else if (smallLayout) {
1253         layoutName = "smallLayout";
1254     } else {
1255         layoutName = "normalLayout";
1256     }
1257
1258     optList = BoardPopUp(squareSize, lineGap, (void*)
1259 #if ENABLE_NLS
1260                                                 &clockFontSet);
1261 #else
1262                                                 clockFontStruct);
1263 #endif
1264     InitDrawingHandle(optList + W_BOARD);
1265     currBoard        = &optList[W_BOARD];
1266     boardWidget      = optList[W_BOARD].handle;
1267     menuBarWidget    = optList[W_MENU].handle;
1268     dropMenu         = optList[W_DROP].handle;
1269     titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1270     formWidget  = XtParent(boardWidget);
1271     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1272     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1273     XtGetValues(optList[W_WHITE].handle, args, 2);
1274     if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1275       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1276       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1277       XtGetValues(optList[W_PAUSE].handle, args, 2);
1278     }
1279     AppendEnginesToMenu(appData.recentEngineList);
1280
1281     xBoardWindow = XtWindow(boardWidget);
1282
1283     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1284     //       not need to go into InitDrawingSizes().
1285
1286     /*
1287      * Create X checkmark bitmap and initialize option menu checks.
1288      */
1289     ReadBitmap(&xMarkPixmap, "checkmark.bm",
1290                checkmark_bits, checkmark_width, checkmark_height);
1291     InitMenuMarkers();
1292
1293     /*
1294      * Create an icon.
1295      */
1296     ReadBitmap(&wIconPixmap, "icon_white.bm",
1297                icon_white_bits, icon_white_width, icon_white_height);
1298     ReadBitmap(&bIconPixmap, "icon_black.bm",
1299                icon_black_bits, icon_black_width, icon_black_height);
1300     iconPixmap = wIconPixmap;
1301     i = 0;
1302     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
1303     XtSetValues(shellWidget, args, i);
1304
1305     /*
1306      * Create a cursor for the board widget.
1307      */
1308     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1309     XChangeWindowAttributes(xDisplay, xBoardWindow,
1310                             CWCursor, &window_attributes);
1311
1312     /*
1313      * Inhibit shell resizing.
1314      */
1315     shellArgs[0].value = (XtArgVal) &w;
1316     shellArgs[1].value = (XtArgVal) &h;
1317     XtGetValues(shellWidget, shellArgs, 2);
1318     shellArgs[4].value = shellArgs[2].value = w;
1319     shellArgs[5].value = shellArgs[3].value = h;
1320 //    XtSetValues(shellWidget, &shellArgs[2], 4);
1321     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1322     marginH =  h - boardHeight;
1323
1324     CatchDeleteWindow(shellWidget, "QuitProc");
1325
1326     CreateAnyPieces();
1327     CreateGrid();
1328
1329     if(appData.logoSize)
1330     {   // locate and read user logo
1331         char buf[MSG_SIZ];
1332         snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1333         ASSIGN(userLogo, buf);
1334     }
1335
1336     if (appData.animate || appData.animateDragging)
1337       CreateAnimVars();
1338
1339     XtAugmentTranslations(formWidget,
1340                           XtParseTranslationTable(globalTranslations));
1341
1342     XtAddEventHandler(formWidget, KeyPressMask, False,
1343                       (XtEventHandler) MoveTypeInProc, NULL);
1344     XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1345                       (XtEventHandler) EventProc, NULL);
1346
1347     /* [AS] Restore layout */
1348     if( wpMoveHistory.visible ) {
1349       HistoryPopUp();
1350     }
1351
1352     if( wpEvalGraph.visible )
1353       {
1354         EvalGraphPopUp();
1355       };
1356
1357     if( wpEngineOutput.visible ) {
1358       EngineOutputPopUp();
1359     }
1360
1361     InitBackEnd2();
1362
1363     if (errorExitStatus == -1) {
1364         if (appData.icsActive) {
1365             /* We now wait until we see "login:" from the ICS before
1366                sending the logon script (problems with timestamp otherwise) */
1367             /*ICSInitScript();*/
1368             if (appData.icsInputBox) ICSInputBoxPopUp();
1369         }
1370
1371     #ifdef SIGWINCH
1372     signal(SIGWINCH, TermSizeSigHandler);
1373     #endif
1374         signal(SIGINT, IntSigHandler);
1375         signal(SIGTERM, IntSigHandler);
1376         if (*appData.cmailGameName != NULLCHAR) {
1377             signal(SIGUSR1, CmailSigHandler);
1378         }
1379     }
1380
1381     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1382     InitPosition(TRUE);
1383     UpdateLogos(TRUE);
1384 //    XtSetKeyboardFocus(shellWidget, formWidget);
1385     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1386
1387     XtAppMainLoop(appContext);
1388     if (appData.debugMode) fclose(debugFP); // [DM] debug
1389     return 0;
1390 }
1391
1392 RETSIGTYPE
1393 TermSizeSigHandler (int sig)
1394 {
1395     update_ics_width();
1396 }
1397
1398 RETSIGTYPE
1399 IntSigHandler (int sig)
1400 {
1401     ExitEvent(sig);
1402 }
1403
1404 RETSIGTYPE
1405 CmailSigHandler (int sig)
1406 {
1407     int dummy = 0;
1408     int error;
1409
1410     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
1411
1412     /* Activate call-back function CmailSigHandlerCallBack()             */
1413     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1414
1415     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1416 }
1417
1418 void
1419 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1420 {
1421     BoardToTop();
1422     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
1423 }
1424 /**** end signal code ****/
1425
1426
1427 #define Abs(n) ((n)<0 ? -(n) : (n))
1428
1429 #ifdef ENABLE_NLS
1430 char *
1431 InsertPxlSize (char *pattern, int targetPxlSize)
1432 {
1433     char *base_fnt_lst, strInt[12], *p, *q;
1434     int alternatives, i, len, strIntLen;
1435
1436     /*
1437      * Replace the "*" (if present) in the pixel-size slot of each
1438      * alternative with the targetPxlSize.
1439      */
1440     p = pattern;
1441     alternatives = 1;
1442     while ((p = strchr(p, ',')) != NULL) {
1443       alternatives++;
1444       p++;
1445     }
1446     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1447     strIntLen = strlen(strInt);
1448     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1449
1450     p = pattern;
1451     q = base_fnt_lst;
1452     while (alternatives--) {
1453       char *comma = strchr(p, ',');
1454       for (i=0; i<14; i++) {
1455         char *hyphen = strchr(p, '-');
1456         if (!hyphen) break;
1457         if (comma && hyphen > comma) break;
1458         len = hyphen + 1 - p;
1459         if (i == 7 && *p == '*' && len == 2) {
1460           p += len;
1461           memcpy(q, strInt, strIntLen);
1462           q += strIntLen;
1463           *q++ = '-';
1464         } else {
1465           memcpy(q, p, len);
1466           p += len;
1467           q += len;
1468         }
1469       }
1470       if (!comma) break;
1471       len = comma + 1 - p;
1472       memcpy(q, p, len);
1473       p += len;
1474       q += len;
1475     }
1476     strcpy(q, p);
1477
1478     return base_fnt_lst;
1479 }
1480
1481 XFontSet
1482 CreateFontSet (char *base_fnt_lst)
1483 {
1484     XFontSet fntSet;
1485     char **missing_list;
1486     int missing_count;
1487     char *def_string;
1488
1489     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1490                             &missing_list, &missing_count, &def_string);
1491     if (appData.debugMode) {
1492       int i, count;
1493       XFontStruct **font_struct_list;
1494       char **font_name_list;
1495       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1496       if (fntSet) {
1497         fprintf(debugFP, " got list %s, locale %s\n",
1498                 XBaseFontNameListOfFontSet(fntSet),
1499                 XLocaleOfFontSet(fntSet));
1500         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1501         for (i = 0; i < count; i++) {
1502           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1503         }
1504       }
1505       for (i = 0; i < missing_count; i++) {
1506         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1507       }
1508     }
1509     if (fntSet == NULL) {
1510       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1511       exit(2);
1512     }
1513     return fntSet;
1514 }
1515 #else // not ENABLE_NLS
1516 /*
1517  * Find a font that matches "pattern" that is as close as
1518  * possible to the targetPxlSize.  Prefer fonts that are k
1519  * pixels smaller to fonts that are k pixels larger.  The
1520  * pattern must be in the X Consortium standard format,
1521  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1522  * The return value should be freed with XtFree when no
1523  * longer needed.
1524  */
1525 char *
1526 FindFont (char *pattern, int targetPxlSize)
1527 {
1528     char **fonts, *p, *best, *scalable, *scalableTail;
1529     int i, j, nfonts, minerr, err, pxlSize;
1530
1531     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1532     if (nfonts < 1) {
1533         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1534                 programName, pattern);
1535         exit(2);
1536     }
1537
1538     best = fonts[0];
1539     scalable = NULL;
1540     minerr = 999999;
1541     for (i=0; i<nfonts; i++) {
1542         j = 0;
1543         p = fonts[i];
1544         if (*p != '-') continue;
1545         while (j < 7) {
1546             if (*p == NULLCHAR) break;
1547             if (*p++ == '-') j++;
1548         }
1549         if (j < 7) continue;
1550         pxlSize = atoi(p);
1551         if (pxlSize == 0) {
1552             scalable = fonts[i];
1553             scalableTail = p;
1554         } else {
1555             err = pxlSize - targetPxlSize;
1556             if (Abs(err) < Abs(minerr) ||
1557                 (minerr > 0 && err < 0 && -err == minerr)) {
1558                 best = fonts[i];
1559                 minerr = err;
1560             }
1561         }
1562     }
1563     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1564         /* If the error is too big and there is a scalable font,
1565            use the scalable font. */
1566         int headlen = scalableTail - scalable;
1567         p = (char *) XtMalloc(strlen(scalable) + 10);
1568         while (isdigit(*scalableTail)) scalableTail++;
1569         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1570     } else {
1571         p = (char *) XtMalloc(strlen(best) + 2);
1572         safeStrCpy(p, best, strlen(best)+1 );
1573     }
1574     if (appData.debugMode) {
1575         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
1576                 pattern, targetPxlSize, p);
1577     }
1578     XFreeFontNames(fonts);
1579     return p;
1580 }
1581 #endif
1582
1583 void
1584 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1585 {
1586     if (bits != NULL) {
1587         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1588                                     wreq, hreq);
1589     }
1590 }
1591
1592 void
1593 MarkMenuItem (char *menuRef, int state)
1594 {
1595     MenuItem *item = MenuNameToItem(menuRef);
1596
1597     if(item) {
1598         Arg args[2];
1599         XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1600         XtSetValues(item->handle, args, 1);
1601     }
1602 }
1603
1604 void
1605 EnableNamedMenuItem (char *menuRef, int state)
1606 {
1607     MenuItem *item = MenuNameToItem(menuRef);
1608
1609     if(item) XtSetSensitive(item->handle, state);
1610 }
1611
1612 void
1613 EnableButtonBar (int state)
1614 {
1615     XtSetSensitive(optList[W_BUTTON].handle, state);
1616 }
1617
1618
1619 void
1620 SetMenuEnables (Enables *enab)
1621 {
1622   while (enab->name != NULL) {
1623     EnableNamedMenuItem(enab->name, enab->value);
1624     enab++;
1625   }
1626 }
1627
1628 void
1629 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1630 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1631     MenuItem *item;
1632     if(*nprms == 0) return;
1633     item = MenuNameToItem(prms[0]);
1634     if(item) ((MenuProc *) item->proc) ();
1635 }
1636
1637 static void
1638 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
1639 {
1640     RecentEngineEvent((int) (intptr_t) addr);
1641 }
1642
1643 void
1644 AppendMenuItem (char *msg, int n)
1645 {
1646     CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
1647 }
1648
1649 void
1650 SetupDropMenu ()
1651 {
1652     int i, j, count;
1653     char label[32];
1654     Arg args[16];
1655     Widget entry;
1656     char* p;
1657
1658     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1659         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1660         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1661                    dmEnables[i].piece);
1662         XtSetSensitive(entry, p != NULL || !appData.testLegality
1663                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1664                                        && !appData.icsActive));
1665         count = 0;
1666         while (p && *p++ == dmEnables[i].piece) count++;
1667         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
1668         j = 0;
1669         XtSetArg(args[j], XtNlabel, label); j++;
1670         XtSetValues(entry, args, j);
1671     }
1672 }
1673
1674 static void
1675 do_flash_delay (unsigned long msec)
1676 {
1677     TimeDelay(msec);
1678 }
1679
1680 void
1681 FlashDelay (int flash_delay)
1682 {
1683         XSync(xDisplay, False);
1684         if(flash_delay) do_flash_delay(flash_delay);
1685 }
1686
1687 double
1688 Fraction (int x, int start, int stop)
1689 {
1690    double f = ((double) x - start)/(stop - start);
1691    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1692    return f;
1693 }
1694
1695 static WindowPlacement wpNew;
1696
1697 void
1698 CoDrag (Widget sh, WindowPlacement *wp)
1699 {
1700     Arg args[16];
1701     int j=0, touch=0, fudge = 2;
1702     GetActualPlacement(sh, wp);
1703     if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x)         < fudge) touch = 1; else // right touch
1704     if(abs(wp->x + wp->width + 2*frameX - wpMain.x)            < fudge) touch = 2; else // left touch
1705     if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1706     if(abs(wp->y + wp->height + frameX + frameY - wpMain.y)    < fudge) touch = 4;      // top touch
1707     if(!touch ) return; // only windows that touch co-move
1708     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1709         int heightInc = wpNew.height - wpMain.height;
1710         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1711         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1712         wp->y += fracTop * heightInc;
1713         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1714         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1715     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1716         int widthInc = wpNew.width - wpMain.width;
1717         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1718         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1719         wp->y += fracLeft * widthInc;
1720         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1721         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1722     }
1723     wp->x += wpNew.x - wpMain.x;
1724     wp->y += wpNew.y - wpMain.y;
1725     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1726     if(touch == 3) wp->y += wpNew.height - wpMain.height;
1727     XtSetArg(args[j], XtNx, wp->x); j++;
1728     XtSetArg(args[j], XtNy, wp->y); j++;
1729     XtSetValues(sh, args, j);
1730 }
1731
1732 void
1733 ReSize (WindowPlacement *wp)
1734 {
1735         int sqx, sqy;
1736         if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1737         sqx = (wp->width  - lineGap - marginW) / BOARD_WIDTH - lineGap;
1738         sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1739         if(sqy < sqx) sqx = sqy;
1740         if(sqx != squareSize) {
1741             squareSize = sqx; // adopt new square size
1742             CreatePNGPieces(); // make newly scaled pieces
1743             InitDrawingSizes(0, 0); // creates grid etc.
1744         } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1745 }
1746
1747 static XtIntervalId delayedDragID = 0;
1748
1749 void
1750 DragProc ()
1751 {
1752         static int busy;
1753         if(busy) return;
1754
1755         busy = 1;
1756         GetActualPlacement(shellWidget, &wpNew);
1757         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1758            wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1759             busy = 0; return; // false alarm
1760         }
1761         ReSize(&wpNew);
1762         if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1763         if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1764         if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1765         if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1766         wpMain = wpNew;
1767         DrawPosition(True, NULL);
1768         delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1769         busy = 0;
1770 }
1771
1772
1773 void
1774 DelayedDrag ()
1775 {
1776     if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1777     delayedDragID =
1778       XtAppAddTimeOut(appContext, 100, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1779 }
1780
1781 void
1782 EventProc (Widget widget, caddr_t unused, XEvent *event)
1783 {
1784     if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1785         DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1786 }
1787
1788 /*
1789  * event handler for redrawing the board
1790  */
1791 void
1792 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1793 {
1794     DrawPosition(True, NULL);
1795 }
1796
1797
1798 void
1799 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1800 {   // [HGM] pv: walk PV
1801     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1802 }
1803
1804 static int savedIndex;  /* gross that this is global */
1805
1806 void
1807 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1808 {
1809         String val;
1810         XawTextPosition index, dummy;
1811         Arg arg;
1812
1813         XawTextGetSelectionPos(w, &index, &dummy);
1814         XtSetArg(arg, XtNstring, &val);
1815         XtGetValues(w, &arg, 1);
1816         ReplaceComment(savedIndex, val);
1817         if(savedIndex != currentMove) ToNrEvent(savedIndex);
1818         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1819 }
1820
1821 void
1822 EditCommentPopUp (int index, char *title, char *text)
1823 {
1824     savedIndex = index;
1825     if (text == NULL) text = "";
1826     NewCommentPopup(title, text, index);
1827 }
1828
1829 void
1830 CommentPopUp (char *title, char *text)
1831 {
1832     savedIndex = currentMove; // [HGM] vari
1833     NewCommentPopup(title, text, currentMove);
1834 }
1835
1836 void
1837 CommentPopDown ()
1838 {
1839     PopDown(CommentDlg);
1840 }
1841
1842
1843 /* Disable all user input other than deleting the window */
1844 static int frozen = 0;
1845
1846 void
1847 FreezeUI ()
1848 {
1849   if (frozen) return;
1850   /* Grab by a widget that doesn't accept input */
1851   XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1852   frozen = 1;
1853 }
1854
1855 /* Undo a FreezeUI */
1856 void
1857 ThawUI ()
1858 {
1859   if (!frozen) return;
1860   XtRemoveGrab(optList[W_MESSG].handle);
1861   frozen = 0;
1862 }
1863
1864 void
1865 ModeHighlight ()
1866 {
1867     Arg args[16];
1868     static int oldPausing = FALSE;
1869     static GameMode oldmode = (GameMode) -1;
1870     char *wname;
1871
1872     if (!boardWidget || !XtIsRealized(boardWidget)) return;
1873
1874     if (pausing != oldPausing) {
1875         oldPausing = pausing;
1876         MarkMenuItem("Mode.Pause", pausing);
1877
1878         if (appData.showButtonBar) {
1879           /* Always toggle, don't set.  Previous code messes up when
1880              invoked while the button is pressed, as releasing it
1881              toggles the state again. */
1882           {
1883             Pixel oldbg, oldfg;
1884             XtSetArg(args[0], XtNbackground, &oldbg);
1885             XtSetArg(args[1], XtNforeground, &oldfg);
1886             XtGetValues(optList[W_PAUSE].handle,
1887                         args, 2);
1888             XtSetArg(args[0], XtNbackground, oldfg);
1889             XtSetArg(args[1], XtNforeground, oldbg);
1890           }
1891           XtSetValues(optList[W_PAUSE].handle, args, 2);
1892         }
1893     }
1894
1895     wname = ModeToWidgetName(oldmode);
1896     if (wname != NULL) {
1897         MarkMenuItem(wname, False);
1898     }
1899     wname = ModeToWidgetName(gameMode);
1900     if (wname != NULL) {
1901         MarkMenuItem(wname, True);
1902     }
1903     oldmode = gameMode;
1904     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1905
1906     /* Maybe all the enables should be handled here, not just this one */
1907     EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1908
1909     DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1910 }
1911
1912
1913 /*
1914  * Button/menu procedures
1915  */
1916
1917 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1918 char *selected_fen_position=NULL;
1919
1920 Boolean
1921 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1922                        Atom *type_return, XtPointer *value_return,
1923                        unsigned long *length_return, int *format_return)
1924 {
1925   char *selection_tmp;
1926
1927 //  if (!selected_fen_position) return False; /* should never happen */
1928   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1929    if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1930     FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1931     long len;
1932     size_t count;
1933     if (f == NULL) return False;
1934     fseek(f, 0, 2);
1935     len = ftell(f);
1936     rewind(f);
1937     selection_tmp = XtMalloc(len + 1);
1938     count = fread(selection_tmp, 1, len, f);
1939     fclose(f);
1940     if (len != count) {
1941       XtFree(selection_tmp);
1942       return False;
1943     }
1944     selection_tmp[len] = NULLCHAR;
1945    } else {
1946     /* note: since no XtSelectionDoneProc was registered, Xt will
1947      * automatically call XtFree on the value returned.  So have to
1948      * make a copy of it allocated with XtMalloc */
1949     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1950     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1951    }
1952
1953     *value_return=selection_tmp;
1954     *length_return=strlen(selection_tmp);
1955     *type_return=*target;
1956     *format_return = 8; /* bits per byte */
1957     return True;
1958   } else if (*target == XA_TARGETS(xDisplay)) {
1959     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1960     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1961     targets_tmp[1] = XA_STRING;
1962     *value_return = targets_tmp;
1963     *type_return = XA_ATOM;
1964     *length_return = 2;
1965 #if 0
1966     // This code leads to a read of value_return out of bounds on 64-bit systems.
1967     // Other code which I have seen always sets *format_return to 32 independent of
1968     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1969     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1970     *format_return = 8 * sizeof(Atom);
1971     if (*format_return > 32) {
1972       *length_return *= *format_return / 32;
1973       *format_return = 32;
1974     }
1975 #else
1976     *format_return = 32;
1977 #endif
1978     return True;
1979   } else {
1980     return False;
1981   }
1982 }
1983
1984 /* note: when called from menu all parameters are NULL, so no clue what the
1985  * Widget which was clicked on was, or what the click event was
1986  */
1987 void
1988 CopySomething (char *src)
1989 {
1990     selected_fen_position = src;
1991     /*
1992      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
1993      * have a notion of a position that is selected but not copied.
1994      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
1995      */
1996     XtOwnSelection(menuBarWidget, XA_PRIMARY,
1997                    CurrentTime,
1998                    SendPositionSelection,
1999                    NULL/* lose_ownership_proc */ ,
2000                    NULL/* transfer_done_proc */);
2001     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2002                    CurrentTime,
2003                    SendPositionSelection,
2004                    NULL/* lose_ownership_proc */ ,
2005                    NULL/* transfer_done_proc */);
2006 }
2007
2008 /* function called when the data to Paste is ready */
2009 static void
2010 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2011                  Atom *type, XtPointer value, unsigned long *len, int *format)
2012 {
2013   char *fenstr=value;
2014   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2015   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2016   EditPositionPasteFEN(fenstr);
2017   XtFree(value);
2018 }
2019
2020 /* called when Paste Position button is pressed,
2021  * all parameters will be NULL */
2022 void
2023 PastePositionProc ()
2024 {
2025     XtGetSelectionValue(menuBarWidget,
2026       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2027       /* (XtSelectionCallbackProc) */ PastePositionCB,
2028       NULL, /* client_data passed to PastePositionCB */
2029
2030       /* better to use the time field from the event that triggered the
2031        * call to this function, but that isn't trivial to get
2032        */
2033       CurrentTime
2034     );
2035     return;
2036 }
2037
2038 /* note: when called from menu all parameters are NULL, so no clue what the
2039  * Widget which was clicked on was, or what the click event was
2040  */
2041 /* function called when the data to Paste is ready */
2042 static void
2043 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2044              Atom *type, XtPointer value, unsigned long *len, int *format)
2045 {
2046   FILE* f;
2047   if (value == NULL || *len == 0) {
2048     return; /* nothing had been selected to copy */
2049   }
2050   f = fopen(gamePasteFilename, "w");
2051   if (f == NULL) {
2052     DisplayError(_("Can't open temp file"), errno);
2053     return;
2054   }
2055   fwrite(value, 1, *len, f);
2056   fclose(f);
2057   XtFree(value);
2058   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2059 }
2060
2061 /* called when Paste Game button is pressed,
2062  * all parameters will be NULL */
2063 void
2064 PasteGameProc ()
2065 {
2066     XtGetSelectionValue(menuBarWidget,
2067       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2068       /* (XtSelectionCallbackProc) */ PasteGameCB,
2069       NULL, /* client_data passed to PasteGameCB */
2070
2071       /* better to use the time field from the event that triggered the
2072        * call to this function, but that isn't trivial to get
2073        */
2074       CurrentTime
2075     );
2076     return;
2077 }
2078
2079
2080 void
2081 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2082 {
2083     QuitProc();
2084 }
2085
2086 int
2087 ShiftKeys ()
2088 {   // bassic primitive for determining if modifier keys are pressed
2089     long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2090     char keys[32];
2091     int i,j,  k=0;
2092     XQueryKeymap(xDisplay,keys);
2093     for(i=0; i<6; i++) {
2094         k <<= 1;
2095         j = XKeysymToKeycode(xDisplay, codes[i]);
2096         k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2097     }
2098     return k;
2099 }
2100
2101 static void
2102 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2103 {
2104     char buf[10];
2105     KeySym sym;
2106     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2107     if ( n == 1 && *buf >= 32 // printable
2108          && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2109         ) BoxAutoPopUp (buf);
2110 }
2111
2112 static void
2113 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2114 {   // [HGM] input: let up-arrow recall previous line from history
2115     IcsKey(1);
2116 }
2117
2118 static void
2119 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2120 {   // [HGM] input: let down-arrow recall next line from history
2121     IcsKey(-1);
2122 }
2123
2124 static void
2125 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2126 {
2127     IcsKey(0);
2128 }
2129
2130 void
2131 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2132 {
2133         if (!TempBackwardActive) {
2134                 TempBackwardActive = True;
2135                 BackwardEvent();
2136         }
2137 }
2138
2139 void
2140 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2141 {
2142         /* Check to see if triggered by a key release event for a repeating key.
2143          * If so the next queued event will be a key press of the same key at the same time */
2144         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2145                 XEvent next;
2146                 XPeekEvent(xDisplay, &next);
2147                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2148                         next.xkey.keycode == event->xkey.keycode)
2149                                 return;
2150         }
2151     ForwardEvent();
2152         TempBackwardActive = False;
2153 }
2154
2155 void
2156 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2157 {   // called as key binding
2158     char buf[MSG_SIZ];
2159     String name;
2160     if (nprms && *nprms > 0)
2161       name = prms[0];
2162     else
2163       name = "xboard";
2164     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2165     system(buf);
2166 }
2167
2168 void
2169 ManProc ()
2170 {   // called from menu
2171     ManInner(NULL, NULL, NULL, NULL);
2172 }
2173
2174 void
2175 SetWindowTitle (char *text, char *title, char *icon)
2176 {
2177     Arg args[16];
2178     int i;
2179     if (appData.titleInWindow) {
2180         i = 0;
2181         XtSetArg(args[i], XtNlabel, text);   i++;
2182         XtSetValues(titleWidget, args, i);
2183     }
2184     i = 0;
2185     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
2186     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
2187     XtSetValues(shellWidget, args, i);
2188     XSync(xDisplay, False);
2189 }
2190
2191
2192 static int
2193 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2194 {
2195     return 0;
2196 }
2197
2198 void
2199 DisplayIcsInteractionTitle (String message)
2200 {
2201   if (oldICSInteractionTitle == NULL) {
2202     /* Magic to find the old window title, adapted from vim */
2203     char *wina = getenv("WINDOWID");
2204     if (wina != NULL) {
2205       Window win = (Window) atoi(wina);
2206       Window root, parent, *children;
2207       unsigned int nchildren;
2208       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2209       for (;;) {
2210         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2211         if (!XQueryTree(xDisplay, win, &root, &parent,
2212                         &children, &nchildren)) break;
2213         if (children) XFree((void *)children);
2214         if (parent == root || parent == 0) break;
2215         win = parent;
2216       }
2217       XSetErrorHandler(oldHandler);
2218     }
2219     if (oldICSInteractionTitle == NULL) {
2220       oldICSInteractionTitle = "xterm";
2221     }
2222   }
2223   printf("\033]0;%s\007", message);
2224   fflush(stdout);
2225 }
2226
2227
2228 XtIntervalId delayedEventTimerXID = 0;
2229 DelayedEventCallback delayedEventCallback = 0;
2230
2231 void
2232 FireDelayedEvent ()
2233 {
2234     delayedEventTimerXID = 0;
2235     delayedEventCallback();
2236 }
2237
2238 void
2239 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2240 {
2241     if(delayedEventTimerXID && delayedEventCallback == cb)
2242         // [HGM] alive: replace, rather than add or flush identical event
2243         XtRemoveTimeOut(delayedEventTimerXID);
2244     delayedEventCallback = cb;
2245     delayedEventTimerXID =
2246       XtAppAddTimeOut(appContext, millisec,
2247                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2248 }
2249
2250 DelayedEventCallback
2251 GetDelayedEvent ()
2252 {
2253   if (delayedEventTimerXID) {
2254     return delayedEventCallback;
2255   } else {
2256     return NULL;
2257   }
2258 }
2259
2260 void
2261 CancelDelayedEvent ()
2262 {
2263   if (delayedEventTimerXID) {
2264     XtRemoveTimeOut(delayedEventTimerXID);
2265     delayedEventTimerXID = 0;
2266   }
2267 }
2268
2269 XtIntervalId loadGameTimerXID = 0;
2270
2271 int
2272 LoadGameTimerRunning ()
2273 {
2274     return loadGameTimerXID != 0;
2275 }
2276
2277 int
2278 StopLoadGameTimer ()
2279 {
2280     if (loadGameTimerXID != 0) {
2281         XtRemoveTimeOut(loadGameTimerXID);
2282         loadGameTimerXID = 0;
2283         return TRUE;
2284     } else {
2285         return FALSE;
2286     }
2287 }
2288
2289 void
2290 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2291 {
2292     loadGameTimerXID = 0;
2293     AutoPlayGameLoop();
2294 }
2295
2296 void
2297 StartLoadGameTimer (long millisec)
2298 {
2299     loadGameTimerXID =
2300       XtAppAddTimeOut(appContext, millisec,
2301                       (XtTimerCallbackProc) LoadGameTimerCallback,
2302                       (XtPointer) 0);
2303 }
2304
2305 XtIntervalId analysisClockXID = 0;
2306
2307 void
2308 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2309 {
2310     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2311          || appData.icsEngineAnalyze) { // [DM]
2312         AnalysisPeriodicEvent(0);
2313         StartAnalysisClock();
2314     }
2315 }
2316
2317 void
2318 StartAnalysisClock ()
2319 {
2320     analysisClockXID =
2321       XtAppAddTimeOut(appContext, 2000,
2322                       (XtTimerCallbackProc) AnalysisClockCallback,
2323                       (XtPointer) 0);
2324 }
2325
2326 XtIntervalId clockTimerXID = 0;
2327
2328 int
2329 ClockTimerRunning ()
2330 {
2331     return clockTimerXID != 0;
2332 }
2333
2334 int
2335 StopClockTimer ()
2336 {
2337     if (clockTimerXID != 0) {
2338         XtRemoveTimeOut(clockTimerXID);
2339         clockTimerXID = 0;
2340         return TRUE;
2341     } else {
2342         return FALSE;
2343     }
2344 }
2345
2346 void
2347 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2348 {
2349     clockTimerXID = 0;
2350     DecrementClocks();
2351 }
2352
2353 void
2354 StartClockTimer (long millisec)
2355 {
2356     clockTimerXID =
2357       XtAppAddTimeOut(appContext, millisec,
2358                       (XtTimerCallbackProc) ClockTimerCallback,
2359                       (XtPointer) 0);
2360 }
2361
2362 void
2363 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2364 {
2365     char buf[MSG_SIZ];
2366     Arg args[16];
2367     Widget w = (Widget) opt->handle;
2368
2369     /* check for low time warning */
2370     Pixel foregroundOrWarningColor = timerForegroundPixel;
2371
2372     if (timer > 0 &&
2373         appData.lowTimeWarning &&
2374         (timer / 1000) < appData.icsAlarmTime)
2375       foregroundOrWarningColor = lowTimeWarningColor;
2376
2377     if (appData.clockMode) {
2378       snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2379       XtSetArg(args[0], XtNlabel, buf);
2380     } else {
2381       snprintf(buf, MSG_SIZ, "%s  ", color);
2382       XtSetArg(args[0], XtNlabel, buf);
2383     }
2384
2385     if (highlight) {
2386
2387         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2388         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2389     } else {
2390         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2391         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2392     }
2393
2394     XtSetValues(w, args, 3);
2395 }
2396
2397 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2398
2399 void
2400 SetClockIcon (int color)
2401 {
2402     Arg args[16];
2403     Pixmap pm = *clockIcons[color];
2404     if (iconPixmap != pm) {
2405         iconPixmap = pm;
2406         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2407         XtSetValues(shellWidget, args, 1);
2408     }
2409 }
2410
2411 void
2412 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2413 {
2414     InputSource *is = (InputSource *) closure;
2415     int count;
2416     int error;
2417     char *p, *q;
2418
2419     if (is->lineByLine) {
2420         count = read(is->fd, is->unused,
2421                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2422         if (count <= 0) {
2423             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2424             return;
2425         }
2426         is->unused += count;
2427         p = is->buf;
2428         while (p < is->unused) {
2429             q = memchr(p, '\n', is->unused - p);
2430             if (q == NULL) break;
2431             q++;
2432             (is->func)(is, is->closure, p, q - p, 0);
2433             p = q;
2434         }
2435         q = is->buf;
2436         while (p < is->unused) {
2437             *q++ = *p++;
2438         }
2439         is->unused = q;
2440     } else {
2441         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2442         if (count == -1)
2443           error = errno;
2444         else
2445           error = 0;
2446         (is->func)(is, is->closure, is->buf, count, error);
2447     }
2448 }
2449
2450 InputSourceRef
2451 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2452 {
2453     InputSource *is;
2454     ChildProc *cp = (ChildProc *) pr;
2455
2456     is = (InputSource *) calloc(1, sizeof(InputSource));
2457     is->lineByLine = lineByLine;
2458     is->func = func;
2459     if (pr == NoProc) {
2460         is->kind = CPReal;
2461         is->fd = fileno(stdin);
2462     } else {
2463         is->kind = cp->kind;
2464         is->fd = cp->fdFrom;
2465     }
2466     if (lineByLine) {
2467         is->unused = is->buf;
2468     }
2469
2470     is->xid = XtAppAddInput(appContext, is->fd,
2471                             (XtPointer) (XtInputReadMask),
2472                             (XtInputCallbackProc) DoInputCallback,
2473                             (XtPointer) is);
2474     is->closure = closure;
2475     return (InputSourceRef) is;
2476 }
2477
2478 void
2479 RemoveInputSource (InputSourceRef isr)
2480 {
2481     InputSource *is = (InputSource *) isr;
2482
2483     if (is->xid == 0) return;
2484     XtRemoveInput(is->xid);
2485     is->xid = 0;
2486 }
2487
2488 #ifndef HAVE_USLEEP
2489
2490 static Boolean frameWaiting;
2491
2492 static RETSIGTYPE
2493 FrameAlarm (int sig)
2494 {
2495   frameWaiting = False;
2496   /* In case System-V style signals.  Needed?? */
2497   signal(SIGALRM, FrameAlarm);
2498 }
2499
2500 void
2501 FrameDelay (int time)
2502 {
2503   struct itimerval delay;
2504
2505   XSync(xDisplay, False);
2506
2507   if (time > 0) {
2508     frameWaiting = True;
2509     signal(SIGALRM, FrameAlarm);
2510     delay.it_interval.tv_sec =
2511       delay.it_value.tv_sec = time / 1000;
2512     delay.it_interval.tv_usec =
2513       delay.it_value.tv_usec = (time % 1000) * 1000;
2514     setitimer(ITIMER_REAL, &delay, NULL);
2515     while (frameWaiting) pause();
2516     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2517     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2518     setitimer(ITIMER_REAL, &delay, NULL);
2519   }
2520 }
2521
2522 #else
2523
2524 void
2525 FrameDelay (int time)
2526 {
2527   XSync(xDisplay, False);
2528   if (time > 0)
2529     usleep(time * 1000);
2530 }
2531
2532 #endif
2533
2534 static void
2535 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2536 {
2537     char buf[MSG_SIZ], *logoName = buf;
2538     if(appData.logo[n][0]) {
2539         logoName = appData.logo[n];
2540     } else if(appData.autoLogo) {
2541         if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2542             sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2543         } else if(appData.directory[n] && appData.directory[n][0]) {
2544             sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2545         }
2546     }
2547     if(logoName[0])
2548         { ASSIGN(cps->programLogo, logoName); }
2549 }
2550
2551 void
2552 UpdateLogos (int displ)
2553 {
2554     if(optList[W_WHITE-1].handle == NULL) return;
2555     LoadLogo(&first, 0, 0);
2556     LoadLogo(&second, 1, appData.icsActive);
2557     if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2558     return;
2559 }
2560