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