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