54f9953146b18a5718a4627b363ce0f3b0f8fe78
[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 cairo_surface_t *
1673 GetOutputSurface(Option *opt, int w, int h)
1674 {
1675         if(w == 0) w = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1676         if(h == 0) h = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1677         return cairo_xlib_surface_create(xDisplay, XtWindow(opt->handle), DefaultVisual(xDisplay, 0), w, h);
1678 }
1679
1680 static void
1681 do_flash_delay (unsigned long msec)
1682 {
1683     TimeDelay(msec);
1684 }
1685
1686 void
1687 FlashDelay (int flash_delay)
1688 {
1689         XSync(xDisplay, False);
1690         if(flash_delay) do_flash_delay(flash_delay);
1691 }
1692
1693 double
1694 Fraction (int x, int start, int stop)
1695 {
1696    double f = ((double) x - start)/(stop - start);
1697    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1698    return f;
1699 }
1700
1701 static WindowPlacement wpNew;
1702
1703 void
1704 CoDrag (Widget sh, WindowPlacement *wp)
1705 {
1706     Arg args[16];
1707     int j=0, touch=0, fudge = 2;
1708     GetActualPlacement(sh, wp);
1709     if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x)         < fudge) touch = 1; else // right touch
1710     if(abs(wp->x + wp->width + 2*frameX - wpMain.x)            < fudge) touch = 2; else // left touch
1711     if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1712     if(abs(wp->y + wp->height + frameX + frameY - wpMain.y)    < fudge) touch = 4;      // top touch
1713     if(!touch ) return; // only windows that touch co-move
1714     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1715         int heightInc = wpNew.height - wpMain.height;
1716         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1717         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1718         wp->y += fracTop * heightInc;
1719         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1720         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1721     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1722         int widthInc = wpNew.width - wpMain.width;
1723         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1724         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1725         wp->y += fracLeft * widthInc;
1726         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1727         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1728     }
1729     wp->x += wpNew.x - wpMain.x;
1730     wp->y += wpNew.y - wpMain.y;
1731     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1732     if(touch == 3) wp->y += wpNew.height - wpMain.height;
1733     XtSetArg(args[j], XtNx, wp->x); j++;
1734     XtSetArg(args[j], XtNy, wp->y); j++;
1735     XtSetValues(sh, args, j);
1736 }
1737
1738 void
1739 ReSize (WindowPlacement *wp)
1740 {
1741         int sqx, sqy;
1742         if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1743         sqx = (wp->width  - lineGap - marginW) / BOARD_WIDTH - lineGap;
1744         sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1745         if(sqy < sqx) sqx = sqy;
1746         if(sqx != squareSize) {
1747             squareSize = sqx; // adopt new square size
1748             NewSurfaces();
1749             CreatePNGPieces(); // make newly scaled pieces
1750             InitDrawingSizes(0, 0); // creates grid etc.
1751         } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1752 }
1753
1754 static XtIntervalId delayedDragID = 0;
1755
1756 void
1757 DragProc ()
1758 {
1759         static int busy;
1760         if(busy) return;
1761
1762         busy = 1;
1763         GetActualPlacement(shellWidget, &wpNew);
1764         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1765            wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1766             busy = 0; return; // false alarm
1767         }
1768         ReSize(&wpNew);
1769         if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1770         if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1771         if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1772         if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1773         wpMain = wpNew;
1774         DrawPosition(True, NULL);
1775         delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1776         busy = 0;
1777 }
1778
1779
1780 void
1781 DelayedDrag ()
1782 {
1783     if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1784     delayedDragID =
1785       XtAppAddTimeOut(appContext, 100, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1786 }
1787
1788 void
1789 EventProc (Widget widget, caddr_t unused, XEvent *event)
1790 {
1791     if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1792         DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1793 }
1794
1795 /*
1796  * event handler for redrawing the board
1797  */
1798 void
1799 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1800 {
1801     DrawPosition(True, NULL);
1802 }
1803
1804
1805 void
1806 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1807 {   // [HGM] pv: walk PV
1808     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1809 }
1810
1811 static int savedIndex;  /* gross that this is global */
1812
1813 void
1814 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1815 {
1816         String val;
1817         XawTextPosition index, dummy;
1818         Arg arg;
1819
1820         XawTextGetSelectionPos(w, &index, &dummy);
1821         XtSetArg(arg, XtNstring, &val);
1822         XtGetValues(w, &arg, 1);
1823         ReplaceComment(savedIndex, val);
1824         if(savedIndex != currentMove) ToNrEvent(savedIndex);
1825         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1826 }
1827
1828 void
1829 EditCommentPopUp (int index, char *title, char *text)
1830 {
1831     savedIndex = index;
1832     if (text == NULL) text = "";
1833     NewCommentPopup(title, text, index);
1834 }
1835
1836 void
1837 CommentPopUp (char *title, char *text)
1838 {
1839     savedIndex = currentMove; // [HGM] vari
1840     NewCommentPopup(title, text, currentMove);
1841 }
1842
1843 void
1844 CommentPopDown ()
1845 {
1846     PopDown(CommentDlg);
1847 }
1848
1849
1850 /* Disable all user input other than deleting the window */
1851 static int frozen = 0;
1852
1853 void
1854 FreezeUI ()
1855 {
1856   if (frozen) return;
1857   /* Grab by a widget that doesn't accept input */
1858   XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1859   frozen = 1;
1860 }
1861
1862 /* Undo a FreezeUI */
1863 void
1864 ThawUI ()
1865 {
1866   if (!frozen) return;
1867   XtRemoveGrab(optList[W_MESSG].handle);
1868   frozen = 0;
1869 }
1870
1871 void
1872 ModeHighlight ()
1873 {
1874     Arg args[16];
1875     static int oldPausing = FALSE;
1876     static GameMode oldmode = (GameMode) -1;
1877     char *wname;
1878
1879     if (!boardWidget || !XtIsRealized(boardWidget)) return;
1880
1881     if (pausing != oldPausing) {
1882         oldPausing = pausing;
1883         MarkMenuItem("Mode.Pause", pausing);
1884
1885         if (appData.showButtonBar) {
1886           /* Always toggle, don't set.  Previous code messes up when
1887              invoked while the button is pressed, as releasing it
1888              toggles the state again. */
1889           {
1890             Pixel oldbg, oldfg;
1891             XtSetArg(args[0], XtNbackground, &oldbg);
1892             XtSetArg(args[1], XtNforeground, &oldfg);
1893             XtGetValues(optList[W_PAUSE].handle,
1894                         args, 2);
1895             XtSetArg(args[0], XtNbackground, oldfg);
1896             XtSetArg(args[1], XtNforeground, oldbg);
1897           }
1898           XtSetValues(optList[W_PAUSE].handle, args, 2);
1899         }
1900     }
1901
1902     wname = ModeToWidgetName(oldmode);
1903     if (wname != NULL) {
1904         MarkMenuItem(wname, False);
1905     }
1906     wname = ModeToWidgetName(gameMode);
1907     if (wname != NULL) {
1908         MarkMenuItem(wname, True);
1909     }
1910     oldmode = gameMode;
1911     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1912
1913     /* Maybe all the enables should be handled here, not just this one */
1914     EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1915
1916     DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
1917 }
1918
1919
1920 /*
1921  * Button/menu procedures
1922  */
1923
1924 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1925 char *selected_fen_position=NULL;
1926
1927 Boolean
1928 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1929                        Atom *type_return, XtPointer *value_return,
1930                        unsigned long *length_return, int *format_return)
1931 {
1932   char *selection_tmp;
1933
1934 //  if (!selected_fen_position) return False; /* should never happen */
1935   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1936    if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1937     FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1938     long len;
1939     size_t count;
1940     if (f == NULL) return False;
1941     fseek(f, 0, 2);
1942     len = ftell(f);
1943     rewind(f);
1944     selection_tmp = XtMalloc(len + 1);
1945     count = fread(selection_tmp, 1, len, f);
1946     fclose(f);
1947     if (len != count) {
1948       XtFree(selection_tmp);
1949       return False;
1950     }
1951     selection_tmp[len] = NULLCHAR;
1952    } else {
1953     /* note: since no XtSelectionDoneProc was registered, Xt will
1954      * automatically call XtFree on the value returned.  So have to
1955      * make a copy of it allocated with XtMalloc */
1956     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1957     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1958    }
1959
1960     *value_return=selection_tmp;
1961     *length_return=strlen(selection_tmp);
1962     *type_return=*target;
1963     *format_return = 8; /* bits per byte */
1964     return True;
1965   } else if (*target == XA_TARGETS(xDisplay)) {
1966     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1967     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1968     targets_tmp[1] = XA_STRING;
1969     *value_return = targets_tmp;
1970     *type_return = XA_ATOM;
1971     *length_return = 2;
1972 #if 0
1973     // This code leads to a read of value_return out of bounds on 64-bit systems.
1974     // Other code which I have seen always sets *format_return to 32 independent of
1975     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1976     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1977     *format_return = 8 * sizeof(Atom);
1978     if (*format_return > 32) {
1979       *length_return *= *format_return / 32;
1980       *format_return = 32;
1981     }
1982 #else
1983     *format_return = 32;
1984 #endif
1985     return True;
1986   } else {
1987     return False;
1988   }
1989 }
1990
1991 /* note: when called from menu all parameters are NULL, so no clue what the
1992  * Widget which was clicked on was, or what the click event was
1993  */
1994 void
1995 CopySomething (char *src)
1996 {
1997     selected_fen_position = src;
1998     /*
1999      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2000      * have a notion of a position that is selected but not copied.
2001      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2002      */
2003     XtOwnSelection(menuBarWidget, XA_PRIMARY,
2004                    CurrentTime,
2005                    SendPositionSelection,
2006                    NULL/* lose_ownership_proc */ ,
2007                    NULL/* transfer_done_proc */);
2008     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2009                    CurrentTime,
2010                    SendPositionSelection,
2011                    NULL/* lose_ownership_proc */ ,
2012                    NULL/* transfer_done_proc */);
2013 }
2014
2015 /* function called when the data to Paste is ready */
2016 static void
2017 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2018                  Atom *type, XtPointer value, unsigned long *len, int *format)
2019 {
2020   char *fenstr=value;
2021   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2022   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2023   EditPositionPasteFEN(fenstr);
2024   XtFree(value);
2025 }
2026
2027 /* called when Paste Position button is pressed,
2028  * all parameters will be NULL */
2029 void
2030 PastePositionProc ()
2031 {
2032     XtGetSelectionValue(menuBarWidget,
2033       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2034       /* (XtSelectionCallbackProc) */ PastePositionCB,
2035       NULL, /* client_data passed to PastePositionCB */
2036
2037       /* better to use the time field from the event that triggered the
2038        * call to this function, but that isn't trivial to get
2039        */
2040       CurrentTime
2041     );
2042     return;
2043 }
2044
2045 /* note: when called from menu all parameters are NULL, so no clue what the
2046  * Widget which was clicked on was, or what the click event was
2047  */
2048 /* function called when the data to Paste is ready */
2049 static void
2050 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2051              Atom *type, XtPointer value, unsigned long *len, int *format)
2052 {
2053   FILE* f;
2054   if (value == NULL || *len == 0) {
2055     return; /* nothing had been selected to copy */
2056   }
2057   f = fopen(gamePasteFilename, "w");
2058   if (f == NULL) {
2059     DisplayError(_("Can't open temp file"), errno);
2060     return;
2061   }
2062   fwrite(value, 1, *len, f);
2063   fclose(f);
2064   XtFree(value);
2065   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2066 }
2067
2068 /* called when Paste Game button is pressed,
2069  * all parameters will be NULL */
2070 void
2071 PasteGameProc ()
2072 {
2073     XtGetSelectionValue(menuBarWidget,
2074       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2075       /* (XtSelectionCallbackProc) */ PasteGameCB,
2076       NULL, /* client_data passed to PasteGameCB */
2077
2078       /* better to use the time field from the event that triggered the
2079        * call to this function, but that isn't trivial to get
2080        */
2081       CurrentTime
2082     );
2083     return;
2084 }
2085
2086
2087 void
2088 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2089 {
2090     QuitProc();
2091 }
2092
2093 int
2094 ShiftKeys ()
2095 {   // bassic primitive for determining if modifier keys are pressed
2096     long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2097     char keys[32];
2098     int i,j,  k=0;
2099     XQueryKeymap(xDisplay,keys);
2100     for(i=0; i<6; i++) {
2101         k <<= 1;
2102         j = XKeysymToKeycode(xDisplay, codes[i]);
2103         k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2104     }
2105     return k;
2106 }
2107
2108 static void
2109 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2110 {
2111     char buf[10];
2112     KeySym sym;
2113     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2114     if ( n == 1 && *buf >= 32 // printable
2115          && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2116         ) BoxAutoPopUp (buf);
2117 }
2118
2119 static void
2120 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2121 {   // [HGM] input: let up-arrow recall previous line from history
2122     IcsKey(1);
2123 }
2124
2125 static void
2126 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2127 {   // [HGM] input: let down-arrow recall next line from history
2128     IcsKey(-1);
2129 }
2130
2131 static void
2132 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2133 {
2134     IcsKey(0);
2135 }
2136
2137 void
2138 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2139 {
2140         if (!TempBackwardActive) {
2141                 TempBackwardActive = True;
2142                 BackwardEvent();
2143         }
2144 }
2145
2146 void
2147 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2148 {
2149         /* Check to see if triggered by a key release event for a repeating key.
2150          * If so the next queued event will be a key press of the same key at the same time */
2151         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2152                 XEvent next;
2153                 XPeekEvent(xDisplay, &next);
2154                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2155                         next.xkey.keycode == event->xkey.keycode)
2156                                 return;
2157         }
2158     ForwardEvent();
2159         TempBackwardActive = False;
2160 }
2161
2162 void
2163 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2164 {   // called as key binding
2165     char buf[MSG_SIZ];
2166     String name;
2167     if (nprms && *nprms > 0)
2168       name = prms[0];
2169     else
2170       name = "xboard";
2171     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2172     system(buf);
2173 }
2174
2175 void
2176 ManProc ()
2177 {   // called from menu
2178     ManInner(NULL, NULL, NULL, NULL);
2179 }
2180
2181 void
2182 SetWindowTitle (char *text, char *title, char *icon)
2183 {
2184     Arg args[16];
2185     int i;
2186     if (appData.titleInWindow) {
2187         i = 0;
2188         XtSetArg(args[i], XtNlabel, text);   i++;
2189         XtSetValues(titleWidget, args, i);
2190     }
2191     i = 0;
2192     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
2193     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
2194     XtSetValues(shellWidget, args, i);
2195     XSync(xDisplay, False);
2196 }
2197
2198
2199 static int
2200 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2201 {
2202     return 0;
2203 }
2204
2205 void
2206 DisplayIcsInteractionTitle (String message)
2207 {
2208   if (oldICSInteractionTitle == NULL) {
2209     /* Magic to find the old window title, adapted from vim */
2210     char *wina = getenv("WINDOWID");
2211     if (wina != NULL) {
2212       Window win = (Window) atoi(wina);
2213       Window root, parent, *children;
2214       unsigned int nchildren;
2215       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2216       for (;;) {
2217         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2218         if (!XQueryTree(xDisplay, win, &root, &parent,
2219                         &children, &nchildren)) break;
2220         if (children) XFree((void *)children);
2221         if (parent == root || parent == 0) break;
2222         win = parent;
2223       }
2224       XSetErrorHandler(oldHandler);
2225     }
2226     if (oldICSInteractionTitle == NULL) {
2227       oldICSInteractionTitle = "xterm";
2228     }
2229   }
2230   printf("\033]0;%s\007", message);
2231   fflush(stdout);
2232 }
2233
2234
2235 XtIntervalId delayedEventTimerXID = 0;
2236 DelayedEventCallback delayedEventCallback = 0;
2237
2238 void
2239 FireDelayedEvent ()
2240 {
2241     delayedEventTimerXID = 0;
2242     delayedEventCallback();
2243 }
2244
2245 void
2246 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2247 {
2248     if(delayedEventTimerXID && delayedEventCallback == cb)
2249         // [HGM] alive: replace, rather than add or flush identical event
2250         XtRemoveTimeOut(delayedEventTimerXID);
2251     delayedEventCallback = cb;
2252     delayedEventTimerXID =
2253       XtAppAddTimeOut(appContext, millisec,
2254                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2255 }
2256
2257 DelayedEventCallback
2258 GetDelayedEvent ()
2259 {
2260   if (delayedEventTimerXID) {
2261     return delayedEventCallback;
2262   } else {
2263     return NULL;
2264   }
2265 }
2266
2267 void
2268 CancelDelayedEvent ()
2269 {
2270   if (delayedEventTimerXID) {
2271     XtRemoveTimeOut(delayedEventTimerXID);
2272     delayedEventTimerXID = 0;
2273   }
2274 }
2275
2276 XtIntervalId loadGameTimerXID = 0;
2277
2278 int
2279 LoadGameTimerRunning ()
2280 {
2281     return loadGameTimerXID != 0;
2282 }
2283
2284 int
2285 StopLoadGameTimer ()
2286 {
2287     if (loadGameTimerXID != 0) {
2288         XtRemoveTimeOut(loadGameTimerXID);
2289         loadGameTimerXID = 0;
2290         return TRUE;
2291     } else {
2292         return FALSE;
2293     }
2294 }
2295
2296 void
2297 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2298 {
2299     loadGameTimerXID = 0;
2300     AutoPlayGameLoop();
2301 }
2302
2303 void
2304 StartLoadGameTimer (long millisec)
2305 {
2306     loadGameTimerXID =
2307       XtAppAddTimeOut(appContext, millisec,
2308                       (XtTimerCallbackProc) LoadGameTimerCallback,
2309                       (XtPointer) 0);
2310 }
2311
2312 XtIntervalId analysisClockXID = 0;
2313
2314 void
2315 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2316 {
2317     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2318          || appData.icsEngineAnalyze) { // [DM]
2319         AnalysisPeriodicEvent(0);
2320         StartAnalysisClock();
2321     }
2322 }
2323
2324 void
2325 StartAnalysisClock ()
2326 {
2327     analysisClockXID =
2328       XtAppAddTimeOut(appContext, 2000,
2329                       (XtTimerCallbackProc) AnalysisClockCallback,
2330                       (XtPointer) 0);
2331 }
2332
2333 XtIntervalId clockTimerXID = 0;
2334
2335 int
2336 ClockTimerRunning ()
2337 {
2338     return clockTimerXID != 0;
2339 }
2340
2341 int
2342 StopClockTimer ()
2343 {
2344     if (clockTimerXID != 0) {
2345         XtRemoveTimeOut(clockTimerXID);
2346         clockTimerXID = 0;
2347         return TRUE;
2348     } else {
2349         return FALSE;
2350     }
2351 }
2352
2353 void
2354 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2355 {
2356     clockTimerXID = 0;
2357     DecrementClocks();
2358 }
2359
2360 void
2361 StartClockTimer (long millisec)
2362 {
2363     clockTimerXID =
2364       XtAppAddTimeOut(appContext, millisec,
2365                       (XtTimerCallbackProc) ClockTimerCallback,
2366                       (XtPointer) 0);
2367 }
2368
2369 void
2370 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2371 {
2372     char buf[MSG_SIZ];
2373     Arg args[16];
2374     Widget w = (Widget) opt->handle;
2375
2376     /* check for low time warning */
2377     Pixel foregroundOrWarningColor = timerForegroundPixel;
2378
2379     if (timer > 0 &&
2380         appData.lowTimeWarning &&
2381         (timer / 1000) < appData.icsAlarmTime)
2382       foregroundOrWarningColor = lowTimeWarningColor;
2383
2384     if (appData.clockMode) {
2385       snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2386       XtSetArg(args[0], XtNlabel, buf);
2387     } else {
2388       snprintf(buf, MSG_SIZ, "%s  ", color);
2389       XtSetArg(args[0], XtNlabel, buf);
2390     }
2391
2392     if (highlight) {
2393
2394         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2395         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2396     } else {
2397         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2398         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2399     }
2400
2401     XtSetValues(w, args, 3);
2402 }
2403
2404 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2405
2406 void
2407 SetClockIcon (int color)
2408 {
2409     Arg args[16];
2410     Pixmap pm = *clockIcons[color];
2411     if (iconPixmap != pm) {
2412         iconPixmap = pm;
2413         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2414         XtSetValues(shellWidget, args, 1);
2415     }
2416 }
2417
2418 void
2419 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2420 {
2421     InputSource *is = (InputSource *) closure;
2422     int count;
2423     int error;
2424     char *p, *q;
2425
2426     if (is->lineByLine) {
2427         count = read(is->fd, is->unused,
2428                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2429         if (count <= 0) {
2430             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2431             return;
2432         }
2433         is->unused += count;
2434         p = is->buf;
2435         while (p < is->unused) {
2436             q = memchr(p, '\n', is->unused - p);
2437             if (q == NULL) break;
2438             q++;
2439             (is->func)(is, is->closure, p, q - p, 0);
2440             p = q;
2441         }
2442         q = is->buf;
2443         while (p < is->unused) {
2444             *q++ = *p++;
2445         }
2446         is->unused = q;
2447     } else {
2448         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2449         if (count == -1)
2450           error = errno;
2451         else
2452           error = 0;
2453         (is->func)(is, is->closure, is->buf, count, error);
2454     }
2455 }
2456
2457 InputSourceRef
2458 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2459 {
2460     InputSource *is;
2461     ChildProc *cp = (ChildProc *) pr;
2462
2463     is = (InputSource *) calloc(1, sizeof(InputSource));
2464     is->lineByLine = lineByLine;
2465     is->func = func;
2466     if (pr == NoProc) {
2467         is->kind = CPReal;
2468         is->fd = fileno(stdin);
2469     } else {
2470         is->kind = cp->kind;
2471         is->fd = cp->fdFrom;
2472     }
2473     if (lineByLine) {
2474         is->unused = is->buf;
2475     }
2476
2477     is->xid = XtAppAddInput(appContext, is->fd,
2478                             (XtPointer) (XtInputReadMask),
2479                             (XtInputCallbackProc) DoInputCallback,
2480                             (XtPointer) is);
2481     is->closure = closure;
2482     return (InputSourceRef) is;
2483 }
2484
2485 void
2486 RemoveInputSource (InputSourceRef isr)
2487 {
2488     InputSource *is = (InputSource *) isr;
2489
2490     if (is->xid == 0) return;
2491     XtRemoveInput(is->xid);
2492     is->xid = 0;
2493 }
2494
2495 #ifndef HAVE_USLEEP
2496
2497 static Boolean frameWaiting;
2498
2499 static RETSIGTYPE
2500 FrameAlarm (int sig)
2501 {
2502   frameWaiting = False;
2503   /* In case System-V style signals.  Needed?? */
2504   signal(SIGALRM, FrameAlarm);
2505 }
2506
2507 void
2508 FrameDelay (int time)
2509 {
2510   struct itimerval delay;
2511
2512   XSync(xDisplay, False);
2513
2514   if (time > 0) {
2515     frameWaiting = True;
2516     signal(SIGALRM, FrameAlarm);
2517     delay.it_interval.tv_sec =
2518       delay.it_value.tv_sec = time / 1000;
2519     delay.it_interval.tv_usec =
2520       delay.it_value.tv_usec = (time % 1000) * 1000;
2521     setitimer(ITIMER_REAL, &delay, NULL);
2522     while (frameWaiting) pause();
2523     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2524     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2525     setitimer(ITIMER_REAL, &delay, NULL);
2526   }
2527 }
2528
2529 #else
2530
2531 void
2532 FrameDelay (int time)
2533 {
2534   XSync(xDisplay, False);
2535   if (time > 0)
2536     usleep(time * 1000);
2537 }
2538
2539 #endif
2540
2541 static void
2542 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2543 {
2544     char buf[MSG_SIZ], *logoName = buf;
2545     if(appData.logo[n][0]) {
2546         logoName = appData.logo[n];
2547     } else if(appData.autoLogo) {
2548         if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2549             sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2550         } else if(appData.directory[n] && appData.directory[n][0]) {
2551             sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2552         }
2553     }
2554     if(logoName[0])
2555         { ASSIGN(cps->programLogo, logoName); }
2556 }
2557
2558 void
2559 UpdateLogos (int displ)
2560 {
2561     if(optList[W_WHITE-1].handle == NULL) return;
2562     LoadLogo(&first, 0, 0);
2563     LoadLogo(&second, 1, appData.icsActive);
2564     if(displ) DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
2565     return;
2566 }
2567