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