Fix initial display of logos
[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     boardWidget      = optList[W_BOARD].handle;
1265     menuBarWidget    = optList[W_MENU].handle;
1266     dropMenu         = optList[W_DROP].handle;
1267     titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1268     formWidget  = XtParent(boardWidget);
1269     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1270     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1271     XtGetValues(optList[W_WHITE].handle, args, 2);
1272     if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1273       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1274       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1275       XtGetValues(optList[W_PAUSE].handle, args, 2);
1276     }
1277     AppendEnginesToMenu(appData.recentEngineList);
1278
1279     xBoardWindow = XtWindow(boardWidget);
1280
1281     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1282     //       not need to go into InitDrawingSizes().
1283
1284     /*
1285      * Create X checkmark bitmap and initialize option menu checks.
1286      */
1287     ReadBitmap(&xMarkPixmap, "checkmark.bm",
1288                checkmark_bits, checkmark_width, checkmark_height);
1289     InitMenuMarkers();
1290
1291     /*
1292      * Create an icon.
1293      */
1294     ReadBitmap(&wIconPixmap, "icon_white.bm",
1295                icon_white_bits, icon_white_width, icon_white_height);
1296     ReadBitmap(&bIconPixmap, "icon_black.bm",
1297                icon_black_bits, icon_black_width, icon_black_height);
1298     iconPixmap = wIconPixmap;
1299     i = 0;
1300     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
1301     XtSetValues(shellWidget, args, i);
1302
1303     /*
1304      * Create a cursor for the board widget.
1305      */
1306     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1307     XChangeWindowAttributes(xDisplay, xBoardWindow,
1308                             CWCursor, &window_attributes);
1309
1310     /*
1311      * Inhibit shell resizing.
1312      */
1313     shellArgs[0].value = (XtArgVal) &w;
1314     shellArgs[1].value = (XtArgVal) &h;
1315     XtGetValues(shellWidget, shellArgs, 2);
1316     shellArgs[4].value = shellArgs[2].value = w;
1317     shellArgs[5].value = shellArgs[3].value = h;
1318 //    XtSetValues(shellWidget, &shellArgs[2], 4);
1319     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1320     marginH =  h - boardHeight;
1321
1322     CatchDeleteWindow(shellWidget, "QuitProc");
1323
1324     CreateAnyPieces();
1325     CreateGrid();
1326
1327     if(appData.logoSize)
1328     {   // locate and read user logo
1329         char buf[MSG_SIZ];
1330         snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1331         ASSIGN(userLogo, buf);
1332     }
1333
1334     if (appData.animate || appData.animateDragging)
1335       CreateAnimVars();
1336
1337     XtAugmentTranslations(formWidget,
1338                           XtParseTranslationTable(globalTranslations));
1339
1340     XtAddEventHandler(formWidget, KeyPressMask, False,
1341                       (XtEventHandler) MoveTypeInProc, NULL);
1342     XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1343                       (XtEventHandler) EventProc, NULL);
1344
1345     /* [AS] Restore layout */
1346     if( wpMoveHistory.visible ) {
1347       HistoryPopUp();
1348     }
1349
1350     if( wpEvalGraph.visible )
1351       {
1352         EvalGraphPopUp();
1353       };
1354
1355     if( wpEngineOutput.visible ) {
1356       EngineOutputPopUp();
1357     }
1358
1359     InitBackEnd2();
1360
1361     if (errorExitStatus == -1) {
1362         if (appData.icsActive) {
1363             /* We now wait until we see "login:" from the ICS before
1364                sending the logon script (problems with timestamp otherwise) */
1365             /*ICSInitScript();*/
1366             if (appData.icsInputBox) ICSInputBoxPopUp();
1367         }
1368
1369     #ifdef SIGWINCH
1370     signal(SIGWINCH, TermSizeSigHandler);
1371     #endif
1372         signal(SIGINT, IntSigHandler);
1373         signal(SIGTERM, IntSigHandler);
1374         if (*appData.cmailGameName != NULLCHAR) {
1375             signal(SIGUSR1, CmailSigHandler);
1376         }
1377     }
1378
1379     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1380     InitPosition(TRUE);
1381     UpdateLogos(TRUE);
1382 //    XtSetKeyboardFocus(shellWidget, formWidget);
1383     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1384
1385     XtAppMainLoop(appContext);
1386     if (appData.debugMode) fclose(debugFP); // [DM] debug
1387     return 0;
1388 }
1389
1390 RETSIGTYPE
1391 TermSizeSigHandler (int sig)
1392 {
1393     update_ics_width();
1394 }
1395
1396 RETSIGTYPE
1397 IntSigHandler (int sig)
1398 {
1399     ExitEvent(sig);
1400 }
1401
1402 RETSIGTYPE
1403 CmailSigHandler (int sig)
1404 {
1405     int dummy = 0;
1406     int error;
1407
1408     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
1409
1410     /* Activate call-back function CmailSigHandlerCallBack()             */
1411     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1412
1413     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1414 }
1415
1416 void
1417 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1418 {
1419     BoardToTop();
1420     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
1421 }
1422 /**** end signal code ****/
1423
1424
1425 #define Abs(n) ((n)<0 ? -(n) : (n))
1426
1427 #ifdef ENABLE_NLS
1428 char *
1429 InsertPxlSize (char *pattern, int targetPxlSize)
1430 {
1431     char *base_fnt_lst, strInt[12], *p, *q;
1432     int alternatives, i, len, strIntLen;
1433
1434     /*
1435      * Replace the "*" (if present) in the pixel-size slot of each
1436      * alternative with the targetPxlSize.
1437      */
1438     p = pattern;
1439     alternatives = 1;
1440     while ((p = strchr(p, ',')) != NULL) {
1441       alternatives++;
1442       p++;
1443     }
1444     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1445     strIntLen = strlen(strInt);
1446     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1447
1448     p = pattern;
1449     q = base_fnt_lst;
1450     while (alternatives--) {
1451       char *comma = strchr(p, ',');
1452       for (i=0; i<14; i++) {
1453         char *hyphen = strchr(p, '-');
1454         if (!hyphen) break;
1455         if (comma && hyphen > comma) break;
1456         len = hyphen + 1 - p;
1457         if (i == 7 && *p == '*' && len == 2) {
1458           p += len;
1459           memcpy(q, strInt, strIntLen);
1460           q += strIntLen;
1461           *q++ = '-';
1462         } else {
1463           memcpy(q, p, len);
1464           p += len;
1465           q += len;
1466         }
1467       }
1468       if (!comma) break;
1469       len = comma + 1 - p;
1470       memcpy(q, p, len);
1471       p += len;
1472       q += len;
1473     }
1474     strcpy(q, p);
1475
1476     return base_fnt_lst;
1477 }
1478
1479 XFontSet
1480 CreateFontSet (char *base_fnt_lst)
1481 {
1482     XFontSet fntSet;
1483     char **missing_list;
1484     int missing_count;
1485     char *def_string;
1486
1487     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1488                             &missing_list, &missing_count, &def_string);
1489     if (appData.debugMode) {
1490       int i, count;
1491       XFontStruct **font_struct_list;
1492       char **font_name_list;
1493       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1494       if (fntSet) {
1495         fprintf(debugFP, " got list %s, locale %s\n",
1496                 XBaseFontNameListOfFontSet(fntSet),
1497                 XLocaleOfFontSet(fntSet));
1498         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1499         for (i = 0; i < count; i++) {
1500           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1501         }
1502       }
1503       for (i = 0; i < missing_count; i++) {
1504         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1505       }
1506     }
1507     if (fntSet == NULL) {
1508       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1509       exit(2);
1510     }
1511     return fntSet;
1512 }
1513 #else // not ENABLE_NLS
1514 /*
1515  * Find a font that matches "pattern" that is as close as
1516  * possible to the targetPxlSize.  Prefer fonts that are k
1517  * pixels smaller to fonts that are k pixels larger.  The
1518  * pattern must be in the X Consortium standard format,
1519  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1520  * The return value should be freed with XtFree when no
1521  * longer needed.
1522  */
1523 char *
1524 FindFont (char *pattern, int targetPxlSize)
1525 {
1526     char **fonts, *p, *best, *scalable, *scalableTail;
1527     int i, j, nfonts, minerr, err, pxlSize;
1528
1529     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1530     if (nfonts < 1) {
1531         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1532                 programName, pattern);
1533         exit(2);
1534     }
1535
1536     best = fonts[0];
1537     scalable = NULL;
1538     minerr = 999999;
1539     for (i=0; i<nfonts; i++) {
1540         j = 0;
1541         p = fonts[i];
1542         if (*p != '-') continue;
1543         while (j < 7) {
1544             if (*p == NULLCHAR) break;
1545             if (*p++ == '-') j++;
1546         }
1547         if (j < 7) continue;
1548         pxlSize = atoi(p);
1549         if (pxlSize == 0) {
1550             scalable = fonts[i];
1551             scalableTail = p;
1552         } else {
1553             err = pxlSize - targetPxlSize;
1554             if (Abs(err) < Abs(minerr) ||
1555                 (minerr > 0 && err < 0 && -err == minerr)) {
1556                 best = fonts[i];
1557                 minerr = err;
1558             }
1559         }
1560     }
1561     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1562         /* If the error is too big and there is a scalable font,
1563            use the scalable font. */
1564         int headlen = scalableTail - scalable;
1565         p = (char *) XtMalloc(strlen(scalable) + 10);
1566         while (isdigit(*scalableTail)) scalableTail++;
1567         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1568     } else {
1569         p = (char *) XtMalloc(strlen(best) + 2);
1570         safeStrCpy(p, best, strlen(best)+1 );
1571     }
1572     if (appData.debugMode) {
1573         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
1574                 pattern, targetPxlSize, p);
1575     }
1576     XFreeFontNames(fonts);
1577     return p;
1578 }
1579 #endif
1580
1581 void
1582 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1583 {
1584     if (bits != NULL) {
1585         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1586                                     wreq, hreq);
1587     }
1588 }
1589
1590 void
1591 MarkMenuItem (char *menuRef, int state)
1592 {
1593     MenuItem *item = MenuNameToItem(menuRef);
1594
1595     if(item) {
1596         Arg args[2];
1597         XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1598         XtSetValues(item->handle, args, 1);
1599     }
1600 }
1601
1602 void
1603 EnableNamedMenuItem (char *menuRef, int state)
1604 {
1605     MenuItem *item = MenuNameToItem(menuRef);
1606
1607     if(item) XtSetSensitive(item->handle, state);
1608 }
1609
1610 void
1611 EnableButtonBar (int state)
1612 {
1613     XtSetSensitive(optList[W_BUTTON].handle, state);
1614 }
1615
1616
1617 void
1618 SetMenuEnables (Enables *enab)
1619 {
1620   while (enab->name != NULL) {
1621     EnableNamedMenuItem(enab->name, enab->value);
1622     enab++;
1623   }
1624 }
1625
1626 void
1627 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1628 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1629     MenuItem *item;
1630     if(*nprms == 0) return;
1631     item = MenuNameToItem(prms[0]);
1632     if(item) ((MenuProc *) item->proc) ();
1633 }
1634
1635 static void
1636 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
1637 {
1638     RecentEngineEvent((int) (intptr_t) addr);
1639 }
1640
1641 void
1642 AppendMenuItem (char *msg, int n)
1643 {
1644     CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
1645 }
1646
1647 void
1648 SetupDropMenu ()
1649 {
1650     int i, j, count;
1651     char label[32];
1652     Arg args[16];
1653     Widget entry;
1654     char* p;
1655
1656     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1657         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1658         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1659                    dmEnables[i].piece);
1660         XtSetSensitive(entry, p != NULL || !appData.testLegality
1661                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1662                                        && !appData.icsActive));
1663         count = 0;
1664         while (p && *p++ == dmEnables[i].piece) count++;
1665         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
1666         j = 0;
1667         XtSetArg(args[j], XtNlabel, label); j++;
1668         XtSetValues(entry, args, j);
1669     }
1670 }
1671
1672 static void
1673 do_flash_delay (unsigned long msec)
1674 {
1675     TimeDelay(msec);
1676 }
1677
1678 void
1679 FlashDelay (int flash_delay)
1680 {
1681         XSync(xDisplay, False);
1682         if(flash_delay) do_flash_delay(flash_delay);
1683 }
1684
1685 double
1686 Fraction (int x, int start, int stop)
1687 {
1688    double f = ((double) x - start)/(stop - start);
1689    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1690    return f;
1691 }
1692
1693 static WindowPlacement wpNew;
1694
1695 void
1696 CoDrag (Widget sh, WindowPlacement *wp)
1697 {
1698     Arg args[16];
1699     int j=0, touch=0, fudge = 2;
1700     GetActualPlacement(sh, wp);
1701     if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x)         < fudge) touch = 1; else // right touch
1702     if(abs(wp->x + wp->width + 2*frameX - wpMain.x)            < fudge) touch = 2; else // left touch
1703     if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1704     if(abs(wp->y + wp->height + frameX + frameY - wpMain.y)    < fudge) touch = 4;      // top touch
1705     if(!touch ) return; // only windows that touch co-move
1706     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1707         int heightInc = wpNew.height - wpMain.height;
1708         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1709         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1710         wp->y += fracTop * heightInc;
1711         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1712         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1713     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1714         int widthInc = wpNew.width - wpMain.width;
1715         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1716         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1717         wp->y += fracLeft * widthInc;
1718         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1719         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1720     }
1721     wp->x += wpNew.x - wpMain.x;
1722     wp->y += wpNew.y - wpMain.y;
1723     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1724     if(touch == 3) wp->y += wpNew.height - wpMain.height;
1725     XtSetArg(args[j], XtNx, wp->x); j++;
1726     XtSetArg(args[j], XtNy, wp->y); j++;
1727     XtSetValues(sh, args, j);
1728 }
1729
1730 void
1731 ReSize (WindowPlacement *wp)
1732 {
1733         int sqx, sqy;
1734         if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1735         sqx = (wp->width  - lineGap - marginW) / BOARD_WIDTH - lineGap;
1736         sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1737         if(sqy < sqx) sqx = sqy;
1738         if(sqx != squareSize) {
1739             squareSize = sqx; // adopt new square size
1740             CreatePNGPieces(); // make newly scaled pieces
1741             InitDrawingSizes(0, 0); // creates grid etc.
1742         } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1743 }
1744
1745 static XtIntervalId delayedDragID = 0;
1746
1747 void
1748 DragProc ()
1749 {
1750         static int busy;
1751         if(busy) return;
1752
1753         busy = 1;
1754         GetActualPlacement(shellWidget, &wpNew);
1755         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1756            wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1757             busy = 0; return; // false alarm
1758         }
1759         ReSize(&wpNew);
1760         if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1761         if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1762         if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1763         if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1764         wpMain = wpNew;
1765         DrawPosition(True, NULL);
1766         delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1767         busy = 0;
1768 }
1769
1770
1771 void
1772 DelayedDrag ()
1773 {
1774     if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1775     delayedDragID =
1776       XtAppAddTimeOut(appContext, 100, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1777 }
1778
1779 void
1780 EventProc (Widget widget, caddr_t unused, XEvent *event)
1781 {
1782     if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1783         DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1784 }
1785
1786 /*
1787  * event handler for redrawing the board
1788  */
1789 void
1790 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1791 {
1792     DrawPosition(True, NULL);
1793 }
1794
1795
1796 void
1797 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1798 {   // [HGM] pv: walk PV
1799     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1800 }
1801
1802 static int savedIndex;  /* gross that this is global */
1803
1804 void
1805 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1806 {
1807         String val;
1808         XawTextPosition index, dummy;
1809         Arg arg;
1810
1811         XawTextGetSelectionPos(w, &index, &dummy);
1812         XtSetArg(arg, XtNstring, &val);
1813         XtGetValues(w, &arg, 1);
1814         ReplaceComment(savedIndex, val);
1815         if(savedIndex != currentMove) ToNrEvent(savedIndex);
1816         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1817 }
1818
1819 void
1820 EditCommentPopUp (int index, char *title, char *text)
1821 {
1822     savedIndex = index;
1823     if (text == NULL) text = "";
1824     NewCommentPopup(title, text, index);
1825 }
1826
1827 void
1828 CommentPopUp (char *title, char *text)
1829 {
1830     savedIndex = currentMove; // [HGM] vari
1831     NewCommentPopup(title, text, currentMove);
1832 }
1833
1834 void
1835 CommentPopDown ()
1836 {
1837     PopDown(CommentDlg);
1838 }
1839
1840
1841 /* Disable all user input other than deleting the window */
1842 static int frozen = 0;
1843
1844 void
1845 FreezeUI ()
1846 {
1847   if (frozen) return;
1848   /* Grab by a widget that doesn't accept input */
1849   XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1850   frozen = 1;
1851 }
1852
1853 /* Undo a FreezeUI */
1854 void
1855 ThawUI ()
1856 {
1857   if (!frozen) return;
1858   XtRemoveGrab(optList[W_MESSG].handle);
1859   frozen = 0;
1860 }
1861
1862 void
1863 ModeHighlight ()
1864 {
1865     Arg args[16];
1866     static int oldPausing = FALSE;
1867     static GameMode oldmode = (GameMode) -1;
1868     char *wname;
1869
1870     if (!boardWidget || !XtIsRealized(boardWidget)) return;
1871
1872     if (pausing != oldPausing) {
1873         oldPausing = pausing;
1874         MarkMenuItem("Mode.Pause", pausing);
1875
1876         if (appData.showButtonBar) {
1877           /* Always toggle, don't set.  Previous code messes up when
1878              invoked while the button is pressed, as releasing it
1879              toggles the state again. */
1880           {
1881             Pixel oldbg, oldfg;
1882             XtSetArg(args[0], XtNbackground, &oldbg);
1883             XtSetArg(args[1], XtNforeground, &oldfg);
1884             XtGetValues(optList[W_PAUSE].handle,
1885                         args, 2);
1886             XtSetArg(args[0], XtNbackground, oldfg);
1887             XtSetArg(args[1], XtNforeground, oldbg);
1888           }
1889           XtSetValues(optList[W_PAUSE].handle, args, 2);
1890         }
1891     }
1892
1893     wname = ModeToWidgetName(oldmode);
1894     if (wname != NULL) {
1895         MarkMenuItem(wname, False);
1896     }
1897     wname = ModeToWidgetName(gameMode);
1898     if (wname != NULL) {
1899         MarkMenuItem(wname, True);
1900     }
1901     oldmode = gameMode;
1902     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1903
1904     /* Maybe all the enables should be handled here, not just this one */
1905     EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1906
1907     DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1908 }
1909
1910
1911 /*
1912  * Button/menu procedures
1913  */
1914
1915 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1916 char *selected_fen_position=NULL;
1917
1918 Boolean
1919 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1920                        Atom *type_return, XtPointer *value_return,
1921                        unsigned long *length_return, int *format_return)
1922 {
1923   char *selection_tmp;
1924
1925 //  if (!selected_fen_position) return False; /* should never happen */
1926   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1927    if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1928     FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1929     long len;
1930     size_t count;
1931     if (f == NULL) return False;
1932     fseek(f, 0, 2);
1933     len = ftell(f);
1934     rewind(f);
1935     selection_tmp = XtMalloc(len + 1);
1936     count = fread(selection_tmp, 1, len, f);
1937     fclose(f);
1938     if (len != count) {
1939       XtFree(selection_tmp);
1940       return False;
1941     }
1942     selection_tmp[len] = NULLCHAR;
1943    } else {
1944     /* note: since no XtSelectionDoneProc was registered, Xt will
1945      * automatically call XtFree on the value returned.  So have to
1946      * make a copy of it allocated with XtMalloc */
1947     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1948     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1949    }
1950
1951     *value_return=selection_tmp;
1952     *length_return=strlen(selection_tmp);
1953     *type_return=*target;
1954     *format_return = 8; /* bits per byte */
1955     return True;
1956   } else if (*target == XA_TARGETS(xDisplay)) {
1957     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1958     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1959     targets_tmp[1] = XA_STRING;
1960     *value_return = targets_tmp;
1961     *type_return = XA_ATOM;
1962     *length_return = 2;
1963 #if 0
1964     // This code leads to a read of value_return out of bounds on 64-bit systems.
1965     // Other code which I have seen always sets *format_return to 32 independent of
1966     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1967     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1968     *format_return = 8 * sizeof(Atom);
1969     if (*format_return > 32) {
1970       *length_return *= *format_return / 32;
1971       *format_return = 32;
1972     }
1973 #else
1974     *format_return = 32;
1975 #endif
1976     return True;
1977   } else {
1978     return False;
1979   }
1980 }
1981
1982 /* note: when called from menu all parameters are NULL, so no clue what the
1983  * Widget which was clicked on was, or what the click event was
1984  */
1985 void
1986 CopySomething (char *src)
1987 {
1988     selected_fen_position = src;
1989     /*
1990      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
1991      * have a notion of a position that is selected but not copied.
1992      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
1993      */
1994     XtOwnSelection(menuBarWidget, XA_PRIMARY,
1995                    CurrentTime,
1996                    SendPositionSelection,
1997                    NULL/* lose_ownership_proc */ ,
1998                    NULL/* transfer_done_proc */);
1999     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2000                    CurrentTime,
2001                    SendPositionSelection,
2002                    NULL/* lose_ownership_proc */ ,
2003                    NULL/* transfer_done_proc */);
2004 }
2005
2006 /* function called when the data to Paste is ready */
2007 static void
2008 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2009                  Atom *type, XtPointer value, unsigned long *len, int *format)
2010 {
2011   char *fenstr=value;
2012   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2013   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2014   EditPositionPasteFEN(fenstr);
2015   XtFree(value);
2016 }
2017
2018 /* called when Paste Position button is pressed,
2019  * all parameters will be NULL */
2020 void
2021 PastePositionProc ()
2022 {
2023     XtGetSelectionValue(menuBarWidget,
2024       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2025       /* (XtSelectionCallbackProc) */ PastePositionCB,
2026       NULL, /* client_data passed to PastePositionCB */
2027
2028       /* better to use the time field from the event that triggered the
2029        * call to this function, but that isn't trivial to get
2030        */
2031       CurrentTime
2032     );
2033     return;
2034 }
2035
2036 /* note: when called from menu all parameters are NULL, so no clue what the
2037  * Widget which was clicked on was, or what the click event was
2038  */
2039 /* function called when the data to Paste is ready */
2040 static void
2041 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2042              Atom *type, XtPointer value, unsigned long *len, int *format)
2043 {
2044   FILE* f;
2045   if (value == NULL || *len == 0) {
2046     return; /* nothing had been selected to copy */
2047   }
2048   f = fopen(gamePasteFilename, "w");
2049   if (f == NULL) {
2050     DisplayError(_("Can't open temp file"), errno);
2051     return;
2052   }
2053   fwrite(value, 1, *len, f);
2054   fclose(f);
2055   XtFree(value);
2056   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2057 }
2058
2059 /* called when Paste Game button is pressed,
2060  * all parameters will be NULL */
2061 void
2062 PasteGameProc ()
2063 {
2064     XtGetSelectionValue(menuBarWidget,
2065       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2066       /* (XtSelectionCallbackProc) */ PasteGameCB,
2067       NULL, /* client_data passed to PasteGameCB */
2068
2069       /* better to use the time field from the event that triggered the
2070        * call to this function, but that isn't trivial to get
2071        */
2072       CurrentTime
2073     );
2074     return;
2075 }
2076
2077
2078 void
2079 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2080 {
2081     QuitProc();
2082 }
2083
2084 int
2085 ShiftKeys ()
2086 {   // bassic primitive for determining if modifier keys are pressed
2087     long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2088     char keys[32];
2089     int i,j,  k=0;
2090     XQueryKeymap(xDisplay,keys);
2091     for(i=0; i<6; i++) {
2092         k <<= 1;
2093         j = XKeysymToKeycode(xDisplay, codes[i]);
2094         k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2095     }
2096     return k;
2097 }
2098
2099 static void
2100 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2101 {
2102     char buf[10];
2103     KeySym sym;
2104     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2105     if ( n == 1 && *buf >= 32 // printable
2106          && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2107         ) BoxAutoPopUp (buf);
2108 }
2109
2110 static void
2111 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2112 {   // [HGM] input: let up-arrow recall previous line from history
2113     IcsKey(1);
2114 }
2115
2116 static void
2117 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2118 {   // [HGM] input: let down-arrow recall next line from history
2119     IcsKey(-1);
2120 }
2121
2122 static void
2123 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2124 {
2125     IcsKey(0);
2126 }
2127
2128 void
2129 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2130 {
2131         if (!TempBackwardActive) {
2132                 TempBackwardActive = True;
2133                 BackwardEvent();
2134         }
2135 }
2136
2137 void
2138 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2139 {
2140         /* Check to see if triggered by a key release event for a repeating key.
2141          * If so the next queued event will be a key press of the same key at the same time */
2142         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2143                 XEvent next;
2144                 XPeekEvent(xDisplay, &next);
2145                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2146                         next.xkey.keycode == event->xkey.keycode)
2147                                 return;
2148         }
2149     ForwardEvent();
2150         TempBackwardActive = False;
2151 }
2152
2153 void
2154 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2155 {   // called as key binding
2156     char buf[MSG_SIZ];
2157     String name;
2158     if (nprms && *nprms > 0)
2159       name = prms[0];
2160     else
2161       name = "xboard";
2162     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2163     system(buf);
2164 }
2165
2166 void
2167 ManProc ()
2168 {   // called from menu
2169     ManInner(NULL, NULL, NULL, NULL);
2170 }
2171
2172 void
2173 SetWindowTitle (char *text, char *title, char *icon)
2174 {
2175     Arg args[16];
2176     int i;
2177     if (appData.titleInWindow) {
2178         i = 0;
2179         XtSetArg(args[i], XtNlabel, text);   i++;
2180         XtSetValues(titleWidget, args, i);
2181     }
2182     i = 0;
2183     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
2184     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
2185     XtSetValues(shellWidget, args, i);
2186     XSync(xDisplay, False);
2187 }
2188
2189
2190 static int
2191 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2192 {
2193     return 0;
2194 }
2195
2196 void
2197 DisplayIcsInteractionTitle (String message)
2198 {
2199   if (oldICSInteractionTitle == NULL) {
2200     /* Magic to find the old window title, adapted from vim */
2201     char *wina = getenv("WINDOWID");
2202     if (wina != NULL) {
2203       Window win = (Window) atoi(wina);
2204       Window root, parent, *children;
2205       unsigned int nchildren;
2206       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2207       for (;;) {
2208         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2209         if (!XQueryTree(xDisplay, win, &root, &parent,
2210                         &children, &nchildren)) break;
2211         if (children) XFree((void *)children);
2212         if (parent == root || parent == 0) break;
2213         win = parent;
2214       }
2215       XSetErrorHandler(oldHandler);
2216     }
2217     if (oldICSInteractionTitle == NULL) {
2218       oldICSInteractionTitle = "xterm";
2219     }
2220   }
2221   printf("\033]0;%s\007", message);
2222   fflush(stdout);
2223 }
2224
2225
2226 XtIntervalId delayedEventTimerXID = 0;
2227 DelayedEventCallback delayedEventCallback = 0;
2228
2229 void
2230 FireDelayedEvent ()
2231 {
2232     delayedEventTimerXID = 0;
2233     delayedEventCallback();
2234 }
2235
2236 void
2237 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2238 {
2239     if(delayedEventTimerXID && delayedEventCallback == cb)
2240         // [HGM] alive: replace, rather than add or flush identical event
2241         XtRemoveTimeOut(delayedEventTimerXID);
2242     delayedEventCallback = cb;
2243     delayedEventTimerXID =
2244       XtAppAddTimeOut(appContext, millisec,
2245                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2246 }
2247
2248 DelayedEventCallback
2249 GetDelayedEvent ()
2250 {
2251   if (delayedEventTimerXID) {
2252     return delayedEventCallback;
2253   } else {
2254     return NULL;
2255   }
2256 }
2257
2258 void
2259 CancelDelayedEvent ()
2260 {
2261   if (delayedEventTimerXID) {
2262     XtRemoveTimeOut(delayedEventTimerXID);
2263     delayedEventTimerXID = 0;
2264   }
2265 }
2266
2267 XtIntervalId loadGameTimerXID = 0;
2268
2269 int
2270 LoadGameTimerRunning ()
2271 {
2272     return loadGameTimerXID != 0;
2273 }
2274
2275 int
2276 StopLoadGameTimer ()
2277 {
2278     if (loadGameTimerXID != 0) {
2279         XtRemoveTimeOut(loadGameTimerXID);
2280         loadGameTimerXID = 0;
2281         return TRUE;
2282     } else {
2283         return FALSE;
2284     }
2285 }
2286
2287 void
2288 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2289 {
2290     loadGameTimerXID = 0;
2291     AutoPlayGameLoop();
2292 }
2293
2294 void
2295 StartLoadGameTimer (long millisec)
2296 {
2297     loadGameTimerXID =
2298       XtAppAddTimeOut(appContext, millisec,
2299                       (XtTimerCallbackProc) LoadGameTimerCallback,
2300                       (XtPointer) 0);
2301 }
2302
2303 XtIntervalId analysisClockXID = 0;
2304
2305 void
2306 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2307 {
2308     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2309          || appData.icsEngineAnalyze) { // [DM]
2310         AnalysisPeriodicEvent(0);
2311         StartAnalysisClock();
2312     }
2313 }
2314
2315 void
2316 StartAnalysisClock ()
2317 {
2318     analysisClockXID =
2319       XtAppAddTimeOut(appContext, 2000,
2320                       (XtTimerCallbackProc) AnalysisClockCallback,
2321                       (XtPointer) 0);
2322 }
2323
2324 XtIntervalId clockTimerXID = 0;
2325
2326 int
2327 ClockTimerRunning ()
2328 {
2329     return clockTimerXID != 0;
2330 }
2331
2332 int
2333 StopClockTimer ()
2334 {
2335     if (clockTimerXID != 0) {
2336         XtRemoveTimeOut(clockTimerXID);
2337         clockTimerXID = 0;
2338         return TRUE;
2339     } else {
2340         return FALSE;
2341     }
2342 }
2343
2344 void
2345 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2346 {
2347     clockTimerXID = 0;
2348     DecrementClocks();
2349 }
2350
2351 void
2352 StartClockTimer (long millisec)
2353 {
2354     clockTimerXID =
2355       XtAppAddTimeOut(appContext, millisec,
2356                       (XtTimerCallbackProc) ClockTimerCallback,
2357                       (XtPointer) 0);
2358 }
2359
2360 void
2361 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2362 {
2363     char buf[MSG_SIZ];
2364     Arg args[16];
2365     Widget w = (Widget) opt->handle;
2366
2367     /* check for low time warning */
2368     Pixel foregroundOrWarningColor = timerForegroundPixel;
2369
2370     if (timer > 0 &&
2371         appData.lowTimeWarning &&
2372         (timer / 1000) < appData.icsAlarmTime)
2373       foregroundOrWarningColor = lowTimeWarningColor;
2374
2375     if (appData.clockMode) {
2376       snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2377       XtSetArg(args[0], XtNlabel, buf);
2378     } else {
2379       snprintf(buf, MSG_SIZ, "%s  ", color);
2380       XtSetArg(args[0], XtNlabel, buf);
2381     }
2382
2383     if (highlight) {
2384
2385         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2386         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2387     } else {
2388         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2389         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2390     }
2391
2392     XtSetValues(w, args, 3);
2393 }
2394
2395 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2396
2397 void
2398 SetClockIcon (int color)
2399 {
2400     Arg args[16];
2401     Pixmap pm = *clockIcons[color];
2402     if (iconPixmap != pm) {
2403         iconPixmap = pm;
2404         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2405         XtSetValues(shellWidget, args, 1);
2406     }
2407 }
2408
2409 void
2410 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2411 {
2412     InputSource *is = (InputSource *) closure;
2413     int count;
2414     int error;
2415     char *p, *q;
2416
2417     if (is->lineByLine) {
2418         count = read(is->fd, is->unused,
2419                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2420         if (count <= 0) {
2421             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2422             return;
2423         }
2424         is->unused += count;
2425         p = is->buf;
2426         while (p < is->unused) {
2427             q = memchr(p, '\n', is->unused - p);
2428             if (q == NULL) break;
2429             q++;
2430             (is->func)(is, is->closure, p, q - p, 0);
2431             p = q;
2432         }
2433         q = is->buf;
2434         while (p < is->unused) {
2435             *q++ = *p++;
2436         }
2437         is->unused = q;
2438     } else {
2439         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2440         if (count == -1)
2441           error = errno;
2442         else
2443           error = 0;
2444         (is->func)(is, is->closure, is->buf, count, error);
2445     }
2446 }
2447
2448 InputSourceRef
2449 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2450 {
2451     InputSource *is;
2452     ChildProc *cp = (ChildProc *) pr;
2453
2454     is = (InputSource *) calloc(1, sizeof(InputSource));
2455     is->lineByLine = lineByLine;
2456     is->func = func;
2457     if (pr == NoProc) {
2458         is->kind = CPReal;
2459         is->fd = fileno(stdin);
2460     } else {
2461         is->kind = cp->kind;
2462         is->fd = cp->fdFrom;
2463     }
2464     if (lineByLine) {
2465         is->unused = is->buf;
2466     }
2467
2468     is->xid = XtAppAddInput(appContext, is->fd,
2469                             (XtPointer) (XtInputReadMask),
2470                             (XtInputCallbackProc) DoInputCallback,
2471                             (XtPointer) is);
2472     is->closure = closure;
2473     return (InputSourceRef) is;
2474 }
2475
2476 void
2477 RemoveInputSource (InputSourceRef isr)
2478 {
2479     InputSource *is = (InputSource *) isr;
2480
2481     if (is->xid == 0) return;
2482     XtRemoveInput(is->xid);
2483     is->xid = 0;
2484 }
2485
2486 #ifndef HAVE_USLEEP
2487
2488 static Boolean frameWaiting;
2489
2490 static RETSIGTYPE
2491 FrameAlarm (int sig)
2492 {
2493   frameWaiting = False;
2494   /* In case System-V style signals.  Needed?? */
2495   signal(SIGALRM, FrameAlarm);
2496 }
2497
2498 void
2499 FrameDelay (int time)
2500 {
2501   struct itimerval delay;
2502
2503   XSync(xDisplay, False);
2504
2505   if (time > 0) {
2506     frameWaiting = True;
2507     signal(SIGALRM, FrameAlarm);
2508     delay.it_interval.tv_sec =
2509       delay.it_value.tv_sec = time / 1000;
2510     delay.it_interval.tv_usec =
2511       delay.it_value.tv_usec = (time % 1000) * 1000;
2512     setitimer(ITIMER_REAL, &delay, NULL);
2513     while (frameWaiting) pause();
2514     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2515     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2516     setitimer(ITIMER_REAL, &delay, NULL);
2517   }
2518 }
2519
2520 #else
2521
2522 void
2523 FrameDelay (int time)
2524 {
2525   XSync(xDisplay, False);
2526   if (time > 0)
2527     usleep(time * 1000);
2528 }
2529
2530 #endif
2531
2532 static void
2533 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2534 {
2535     char buf[MSG_SIZ], *logoName = buf;
2536     if(appData.logo[n][0]) {
2537         logoName = appData.logo[n];
2538     } else if(appData.autoLogo) {
2539         if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2540             sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2541         } else if(appData.directory[n] && appData.directory[n][0]) {
2542             sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2543         }
2544     }
2545     if(logoName[0])
2546         { ASSIGN(cps->programLogo, logoName); }
2547 }
2548
2549 void
2550 UpdateLogos (int displ)
2551 {
2552     if(optList[W_WHITE-1].handle == NULL) return;
2553     LoadLogo(&first, 0, 0);
2554     LoadLogo(&second, 1, appData.icsActive);
2555     if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2556     return;
2557 }
2558