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