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