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