Remember window params of slave board
[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() 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 ArgFilename: p = " FILE"; break;
938     case ArgX:        p = " Nx"; break;
939     case ArgY:        p = " Ny"; break;
940     case ArgAttribs:  p = " TEXTCOL"; break;
941     case ArgColor:    p = " COL"; break;
942     case ArgFont:     p = " FONT"; break;
943     case ArgBoardSize: p = " SIZE"; break;
944     case ArgFloat: p = " FLOAT"; break;
945     case ArgTrue:
946     case ArgFalse:
947     case ArgTwo:
948     case ArgNone:
949     case ArgCommSettings:
950       break;
951   }
952   return p;
953 }
954
955 char *
956 GenerateGlobalTranslationTable (void)
957 {
958   /* go through all menu items and extract the keyboard shortcuts, so that X11 can load them */
959   char *output;
960
961   int i,j;
962   MenuItem *mi;
963
964   output = strdup("");
965
966   /* loop over all menu entries */
967   for( i=0; menuBar[i].mi ; i++)
968     {
969       mi = menuBar[i].mi;
970       for(j=0; mi[j].proc; j++)
971         {
972           if (mi[j].accel)
973             {
974               int ctrl  = 0;
975               int shift = 0;
976               int alt   = 0;
977
978               char *key,*test, *mods;
979
980               /* check for Ctrl/Alt */
981               if( strstr(mi[j].accel, "<Ctrl>")  ) ctrl  = 1;
982               if( strstr(mi[j].accel, "<Shift>") ) shift = 1;
983               if( strstr(mi[j].accel, "<Alt>")   ) alt   = 1;
984
985               /* remove all <...> */
986               test = strrchr(mi[j].accel, '>');
987               if ( test==NULL )
988                 key = strdup(mi[j].accel);
989               else
990                 key = strdup(++test); // remove ">"
991
992               /* instead of shift X11 uses the uppercase letter directly*/
993               if (shift && strlen(key)==1 )
994                 {
995                   *key  = toupper(*key);
996                   shift = 0;
997                 }
998
999               /* handle some special cases which have different names in X11 */
1000               if ( strncmp(key, "Page_Down", 9) == 0 )
1001                 {
1002                   free(key);
1003                   key=strdup("Next");
1004                 }
1005               else if ( strncmp(key, "Page_Up", 7) == 0 )
1006                 {
1007                   free(key);
1008                   key=strdup("Prior");
1009                 };
1010
1011               /* create string of mods */
1012               if (ctrl)
1013                 mods = strdup("Ctrl ");
1014               else
1015                 mods = strdup("");
1016
1017               if(alt)
1018                 {
1019                   mods = realloc(mods, strlen(mods) + strlen("Meta ")+1);
1020                   strncat(mods, "Meta ", 5);
1021                 };
1022
1023               if(shift)
1024                 {
1025                   mods = realloc(mods, strlen(mods) + strlen("Shift ")+1);
1026                   strncat(mods, "Shift ", 6);
1027                 };
1028
1029               // remove trailing space
1030               if( isspace(mods[strlen(mods)-1]) )
1031                 mods[strlen(mods)-1]='\0';
1032
1033               /* get the name for the callback, we can use MenuItem() here that will call KeyBindingProc */
1034               size_t namesize = snprintf(NULL, 0, "%s.%s", menuBar[i].ref, mi[j].ref);
1035               char *name = malloc(namesize+1);
1036               snprintf(name, namesize+1, "%s.%s", menuBar[i].ref, mi[j].ref);
1037
1038               size_t buffersize = snprintf(NULL, 0, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
1039               char *buffer = malloc(buffersize+1);
1040               snprintf(buffer, buffersize+1, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
1041
1042               /* add string to the output */
1043               output = realloc(output, strlen(output) + strlen(buffer)+1);
1044               strncat(output, buffer, strlen(buffer));
1045
1046               /* clean up */
1047               free(key);
1048               free(buffer);
1049               free(name);
1050               free(mods);
1051             }
1052         }
1053     }
1054   return output;
1055 }
1056
1057
1058 void
1059 PrintOptions ()
1060 {
1061   char buf[MSG_SIZ];
1062   int len=0;
1063   ArgDescriptor *q, *p = argDescriptors+5;
1064   printf("\nXBoard accepts the following options:\n"
1065          "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1066          " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1067          " SIZE = board-size spec(s)\n"
1068          " Within parentheses are short forms, or options to set to true or false.\n"
1069          " Persistent options (saved in the settings file) are marked with *)\n\n");
1070   while(p->argName) {
1071     if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1072     snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1073     if(p->save) strcat(buf+len, "*");
1074     for(q=p+1; q->argLoc == p->argLoc; q++) {
1075       if(q->argName[0] == '-') continue;
1076       strcat(buf+len, q == p+1 ? " (" : " ");
1077       sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1078     }
1079     if(q != p+1) strcat(buf+len, ")");
1080     len = strlen(buf);
1081     if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1082     p = q;
1083   }
1084   if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1085 }
1086
1087 int
1088 main (int argc, char **argv)
1089 {
1090     int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1091     XSetWindowAttributes window_attributes;
1092     Arg args[16];
1093     Dimension boardWidth, boardHeight, w, h;
1094     char *p;
1095     int forceMono = False;
1096
1097     srandom(time(0)); // [HGM] book: make random truly random
1098
1099     setbuf(stdout, NULL);
1100     setbuf(stderr, NULL);
1101     debugFP = stderr;
1102
1103     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1104         printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1105         exit(0);
1106     }
1107
1108     if(argc > 1 && !strcmp(argv[1], "--help" )) {
1109         PrintOptions();
1110         exit(0);
1111     }
1112
1113     programName = strrchr(argv[0], '/');
1114     if (programName == NULL)
1115       programName = argv[0];
1116     else
1117       programName++;
1118
1119 #ifdef ENABLE_NLS
1120     XtSetLanguageProc(NULL, NULL, NULL);
1121     if (appData.debugMode) {
1122       fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1123     }
1124
1125     bindtextdomain(PACKAGE, LOCALEDIR);
1126     textdomain(PACKAGE);
1127 #endif
1128
1129     appData.boardSize = "";
1130     InitAppData(ConvertToLine(argc, argv));
1131     p = getenv("HOME");
1132     if (p == NULL) p = "/tmp";
1133     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1134     gameCopyFilename = (char*) malloc(i);
1135     gamePasteFilename = (char*) malloc(i);
1136     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1137     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1138
1139     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1140         static char buf[MSG_SIZ];
1141         EscapeExpand(buf, appData.firstInitString);
1142         appData.firstInitString = strdup(buf);
1143         EscapeExpand(buf, appData.secondInitString);
1144         appData.secondInitString = strdup(buf);
1145         EscapeExpand(buf, appData.firstComputerString);
1146         appData.firstComputerString = strdup(buf);
1147         EscapeExpand(buf, appData.secondComputerString);
1148         appData.secondComputerString = strdup(buf);
1149     }
1150
1151     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1152         chessDir = ".";
1153     } else {
1154         if (chdir(chessDir) != 0) {
1155             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1156             perror(chessDir);
1157             exit(1);
1158         }
1159     }
1160
1161     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1162         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1163         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
1164            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1165            exit(errno);
1166         }
1167         setbuf(debugFP, NULL);
1168     }
1169
1170     /* [HGM,HR] make sure board size is acceptable */
1171     if(appData.NrFiles > BOARD_FILES ||
1172        appData.NrRanks > BOARD_RANKS   )
1173          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1174
1175 #if !HIGHDRAG
1176     /* This feature does not work; animation needs a rewrite */
1177     appData.highlightDragging = FALSE;
1178 #endif
1179     InitBackEnd1();
1180
1181         gameInfo.variant = StringToVariant(appData.variant);
1182         InitPosition(FALSE);
1183
1184     shellWidget =
1185       XtAppInitialize(&appContext, "XBoard", shellOptions,
1186                       XtNumber(shellOptions),
1187                       &argc, argv, xboardResources, NULL, 0);
1188
1189     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1190                               clientResources, XtNumber(clientResources),
1191                               NULL, 0);
1192
1193     xDisplay = XtDisplay(shellWidget);
1194     xScreen = DefaultScreen(xDisplay);
1195     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1196
1197     /*
1198      * determine size, based on supplied or remembered -size, or screen size
1199      */
1200     if (isdigit(appData.boardSize[0])) {
1201         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1202                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1203                    &fontPxlSize, &smallLayout, &tinyLayout);
1204         if (i == 0) {
1205             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1206                     programName, appData.boardSize);
1207             exit(2);
1208         }
1209         if (i < 7) {
1210             /* Find some defaults; use the nearest known size */
1211             SizeDefaults *szd, *nearest;
1212             int distance = 99999;
1213             nearest = szd = sizeDefaults;
1214             while (szd->name != NULL) {
1215                 if (abs(szd->squareSize - squareSize) < distance) {
1216                     nearest = szd;
1217                     distance = abs(szd->squareSize - squareSize);
1218                     if (distance == 0) break;
1219                 }
1220                 szd++;
1221             }
1222             if (i < 2) lineGap = nearest->lineGap;
1223             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1224             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1225             if (i < 5) fontPxlSize = nearest->fontPxlSize;
1226             if (i < 6) smallLayout = nearest->smallLayout;
1227             if (i < 7) tinyLayout = nearest->tinyLayout;
1228         }
1229     } else {
1230         SizeDefaults *szd = sizeDefaults;
1231         if (*appData.boardSize == NULLCHAR) {
1232             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1233                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1234               szd++;
1235             }
1236             if (szd->name == NULL) szd--;
1237             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1238         } else {
1239             while (szd->name != NULL &&
1240                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1241             if (szd->name == NULL) {
1242                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1243                         programName, appData.boardSize);
1244                 exit(2);
1245             }
1246         }
1247         squareSize = szd->squareSize;
1248         lineGap = szd->lineGap;
1249         clockFontPxlSize = szd->clockFontPxlSize;
1250         coordFontPxlSize = szd->coordFontPxlSize;
1251         fontPxlSize = szd->fontPxlSize;
1252         smallLayout = szd->smallLayout;
1253         tinyLayout = szd->tinyLayout;
1254         // [HGM] font: use defaults from settings file if available and not overruled
1255     }
1256
1257     defaultLineGap = lineGap;
1258     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1259
1260     /* [HR] height treated separately (hacked) */
1261     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1262     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1263
1264     /*
1265      * Determine what fonts to use.
1266      */
1267     InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1268
1269     /*
1270      * Detect if there are not enough colors available and adapt.
1271      */
1272     if (DefaultDepth(xDisplay, xScreen) <= 2) {
1273       appData.monoMode = True;
1274     }
1275
1276     forceMono = MakeColors();
1277
1278     if (forceMono) {
1279       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1280               programName);
1281         appData.monoMode = True;
1282     }
1283
1284     if (appData.monoMode && appData.debugMode) {
1285         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1286                 (unsigned long) XWhitePixel(xDisplay, xScreen),
1287                 (unsigned long) XBlackPixel(xDisplay, xScreen));
1288     }
1289
1290     ParseIcsTextColors();
1291
1292     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1293
1294     /*
1295      * widget hierarchy
1296      */
1297     if (tinyLayout) {
1298         layoutName = "tinyLayout";
1299     } else if (smallLayout) {
1300         layoutName = "smallLayout";
1301     } else {
1302         layoutName = "normalLayout";
1303     }
1304
1305     optList = BoardPopUp(squareSize, lineGap, (void*)
1306 #if ENABLE_NLS
1307                                                 &clockFontSet);
1308 #else
1309                                                 clockFontStruct);
1310 #endif
1311     InitDrawingHandle(optList + W_BOARD);
1312     currBoard        = &optList[W_BOARD];
1313     boardWidget      = optList[W_BOARD].handle;
1314     menuBarWidget    = optList[W_MENU].handle;
1315     dropMenu         = optList[W_DROP].handle;
1316     titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1317     formWidget  = XtParent(boardWidget);
1318     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1319     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1320     XtGetValues(optList[W_WHITE].handle, args, 2);
1321     if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1322       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1323       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1324       XtGetValues(optList[W_PAUSE].handle, args, 2);
1325     }
1326
1327     xBoardWindow = XtWindow(boardWidget);
1328
1329     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1330     //       not need to go into InitDrawingSizes().
1331
1332     /*
1333      * Create X checkmark bitmap and initialize option menu checks.
1334      */
1335     ReadBitmap(&xMarkPixmap, "checkmark.bm",
1336                checkmark_bits, checkmark_width, checkmark_height);
1337     InitMenuMarkers();
1338
1339     /*
1340      * Create an icon.
1341      */
1342     ReadBitmap(&wIconPixmap, "icon_white.bm",
1343                icon_white_bits, icon_white_width, icon_white_height);
1344     ReadBitmap(&bIconPixmap, "icon_black.bm",
1345                icon_black_bits, icon_black_width, icon_black_height);
1346     iconPixmap = wIconPixmap;
1347     i = 0;
1348     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
1349     XtSetValues(shellWidget, args, i);
1350
1351     /*
1352      * Create a cursor for the board widget.
1353      */
1354     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1355     XChangeWindowAttributes(xDisplay, xBoardWindow,
1356                             CWCursor, &window_attributes);
1357
1358     /*
1359      * Inhibit shell resizing.
1360      */
1361     shellArgs[0].value = (XtArgVal) &w;
1362     shellArgs[1].value = (XtArgVal) &h;
1363     XtGetValues(shellWidget, shellArgs, 2);
1364     shellArgs[4].value = shellArgs[2].value = w;
1365     shellArgs[5].value = shellArgs[3].value = h;
1366 //    XtSetValues(shellWidget, &shellArgs[2], 4);
1367     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1368     marginH =  h - boardHeight;
1369
1370     CatchDeleteWindow(shellWidget, "QuitProc");
1371
1372     CreateAnyPieces();
1373     CreateGrid();
1374
1375     if(appData.logoSize)
1376     {   // locate and read user logo
1377         char buf[MSG_SIZ];
1378         snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1379         ASSIGN(userLogo, buf);
1380     }
1381
1382     if (appData.animate || appData.animateDragging)
1383       CreateAnimVars();
1384
1385
1386     char *TranslationsTableMenus=GenerateGlobalTranslationTable ();
1387
1388     XtAugmentTranslations(formWidget,
1389                           XtParseTranslationTable(globalTranslations));
1390     XtAugmentTranslations(formWidget,
1391                           XtParseTranslationTable(TranslationsTableMenus));
1392
1393     XtAddEventHandler(formWidget, KeyPressMask, False,
1394                       (XtEventHandler) MoveTypeInProc, NULL);
1395     XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1396                       (XtEventHandler) EventProc, NULL);
1397
1398     /* [AS] Restore layout */
1399     if( wpMoveHistory.visible ) {
1400       HistoryPopUp();
1401     }
1402
1403     if( wpEvalGraph.visible )
1404       {
1405         EvalGraphPopUp();
1406       };
1407
1408     if( wpEngineOutput.visible ) {
1409       EngineOutputPopUp();
1410     }
1411
1412     InitBackEnd2();
1413
1414     if (errorExitStatus == -1) {
1415         if (appData.icsActive) {
1416             /* We now wait until we see "login:" from the ICS before
1417                sending the logon script (problems with timestamp otherwise) */
1418             /*ICSInitScript();*/
1419             if (appData.icsInputBox) ICSInputBoxPopUp();
1420         }
1421
1422     #ifdef SIGWINCH
1423     signal(SIGWINCH, TermSizeSigHandler);
1424     #endif
1425         signal(SIGINT, IntSigHandler);
1426         signal(SIGTERM, IntSigHandler);
1427         if (*appData.cmailGameName != NULLCHAR) {
1428             signal(SIGUSR1, CmailSigHandler);
1429         }
1430     }
1431
1432     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1433     InitPosition(TRUE);
1434     UpdateLogos(TRUE);
1435 //    XtSetKeyboardFocus(shellWidget, formWidget);
1436     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1437
1438     XtAppMainLoop(appContext);
1439     if (appData.debugMode) fclose(debugFP); // [DM] debug
1440     return 0;
1441 }
1442
1443 RETSIGTYPE
1444 TermSizeSigHandler (int sig)
1445 {
1446     update_ics_width();
1447 }
1448
1449 RETSIGTYPE
1450 IntSigHandler (int sig)
1451 {
1452     ExitEvent(sig);
1453 }
1454
1455 RETSIGTYPE
1456 CmailSigHandler (int sig)
1457 {
1458     int dummy = 0;
1459     int error;
1460
1461     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
1462
1463     /* Activate call-back function CmailSigHandlerCallBack()             */
1464     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1465
1466     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1467 }
1468
1469 void
1470 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1471 {
1472     BoardToTop();
1473     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
1474 }
1475 /**** end signal code ****/
1476
1477
1478 #define Abs(n) ((n)<0 ? -(n) : (n))
1479
1480 #ifdef ENABLE_NLS
1481 char *
1482 InsertPxlSize (char *pattern, int targetPxlSize)
1483 {
1484     char *base_fnt_lst, strInt[12], *p, *q;
1485     int alternatives, i, len, strIntLen;
1486
1487     /*
1488      * Replace the "*" (if present) in the pixel-size slot of each
1489      * alternative with the targetPxlSize.
1490      */
1491     p = pattern;
1492     alternatives = 1;
1493     while ((p = strchr(p, ',')) != NULL) {
1494       alternatives++;
1495       p++;
1496     }
1497     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1498     strIntLen = strlen(strInt);
1499     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1500
1501     p = pattern;
1502     q = base_fnt_lst;
1503     while (alternatives--) {
1504       char *comma = strchr(p, ',');
1505       for (i=0; i<14; i++) {
1506         char *hyphen = strchr(p, '-');
1507         if (!hyphen) break;
1508         if (comma && hyphen > comma) break;
1509         len = hyphen + 1 - p;
1510         if (i == 7 && *p == '*' && len == 2) {
1511           p += len;
1512           memcpy(q, strInt, strIntLen);
1513           q += strIntLen;
1514           *q++ = '-';
1515         } else {
1516           memcpy(q, p, len);
1517           p += len;
1518           q += len;
1519         }
1520       }
1521       if (!comma) break;
1522       len = comma + 1 - p;
1523       memcpy(q, p, len);
1524       p += len;
1525       q += len;
1526     }
1527     strcpy(q, p);
1528
1529     return base_fnt_lst;
1530 }
1531
1532 XFontSet
1533 CreateFontSet (char *base_fnt_lst)
1534 {
1535     XFontSet fntSet;
1536     char **missing_list;
1537     int missing_count;
1538     char *def_string;
1539
1540     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1541                             &missing_list, &missing_count, &def_string);
1542     if (appData.debugMode) {
1543       int i, count;
1544       XFontStruct **font_struct_list;
1545       char **font_name_list;
1546       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1547       if (fntSet) {
1548         fprintf(debugFP, " got list %s, locale %s\n",
1549                 XBaseFontNameListOfFontSet(fntSet),
1550                 XLocaleOfFontSet(fntSet));
1551         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1552         for (i = 0; i < count; i++) {
1553           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1554         }
1555       }
1556       for (i = 0; i < missing_count; i++) {
1557         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1558       }
1559     }
1560     if (fntSet == NULL) {
1561       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1562       exit(2);
1563     }
1564     return fntSet;
1565 }
1566 #else // not ENABLE_NLS
1567 /*
1568  * Find a font that matches "pattern" that is as close as
1569  * possible to the targetPxlSize.  Prefer fonts that are k
1570  * pixels smaller to fonts that are k pixels larger.  The
1571  * pattern must be in the X Consortium standard format,
1572  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1573  * The return value should be freed with XtFree when no
1574  * longer needed.
1575  */
1576 char *
1577 FindFont (char *pattern, int targetPxlSize)
1578 {
1579     char **fonts, *p, *best, *scalable, *scalableTail;
1580     int i, j, nfonts, minerr, err, pxlSize;
1581
1582     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1583     if (nfonts < 1) {
1584         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1585                 programName, pattern);
1586         exit(2);
1587     }
1588
1589     best = fonts[0];
1590     scalable = NULL;
1591     minerr = 999999;
1592     for (i=0; i<nfonts; i++) {
1593         j = 0;
1594         p = fonts[i];
1595         if (*p != '-') continue;
1596         while (j < 7) {
1597             if (*p == NULLCHAR) break;
1598             if (*p++ == '-') j++;
1599         }
1600         if (j < 7) continue;
1601         pxlSize = atoi(p);
1602         if (pxlSize == 0) {
1603             scalable = fonts[i];
1604             scalableTail = p;
1605         } else {
1606             err = pxlSize - targetPxlSize;
1607             if (Abs(err) < Abs(minerr) ||
1608                 (minerr > 0 && err < 0 && -err == minerr)) {
1609                 best = fonts[i];
1610                 minerr = err;
1611             }
1612         }
1613     }
1614     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1615         /* If the error is too big and there is a scalable font,
1616            use the scalable font. */
1617         int headlen = scalableTail - scalable;
1618         p = (char *) XtMalloc(strlen(scalable) + 10);
1619         while (isdigit(*scalableTail)) scalableTail++;
1620         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1621     } else {
1622         p = (char *) XtMalloc(strlen(best) + 2);
1623         safeStrCpy(p, best, strlen(best)+1 );
1624     }
1625     if (appData.debugMode) {
1626         fprintf(debugFP, "resolved %s at pixel size %d\n  to %s\n",
1627                 pattern, targetPxlSize, p);
1628     }
1629     XFreeFontNames(fonts);
1630     return p;
1631 }
1632 #endif
1633
1634 void
1635 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1636 {
1637     if (bits != NULL) {
1638         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1639                                     wreq, hreq);
1640     }
1641 }
1642
1643 void
1644 MarkMenuItem (char *menuRef, int state)
1645 {
1646     MenuItem *item = MenuNameToItem(menuRef);
1647
1648     if(item) {
1649         Arg args[2];
1650         XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1651         XtSetValues(item->handle, args, 1);
1652     }
1653 }
1654
1655 void
1656 EnableNamedMenuItem (char *menuRef, int state)
1657 {
1658     MenuItem *item = MenuNameToItem(menuRef);
1659
1660     if(item) XtSetSensitive(item->handle, state);
1661 }
1662
1663 void
1664 EnableButtonBar (int state)
1665 {
1666     XtSetSensitive(optList[W_BUTTON].handle, state);
1667 }
1668
1669
1670 void
1671 SetMenuEnables (Enables *enab)
1672 {
1673   while (enab->name != NULL) {
1674     EnableNamedMenuItem(enab->name, enab->value);
1675     enab++;
1676   }
1677 }
1678
1679 void
1680 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1681 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1682     MenuItem *item;
1683     if(*nprms == 0) return;
1684     item = MenuNameToItem(prms[0]);
1685     if(item) ((MenuProc *) item->proc) ();
1686 }
1687
1688 void
1689 SetupDropMenu ()
1690 {
1691     int i, j, count;
1692     char label[32];
1693     Arg args[16];
1694     Widget entry;
1695     char* p;
1696
1697     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1698         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1699         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1700                    dmEnables[i].piece);
1701         XtSetSensitive(entry, p != NULL || !appData.testLegality
1702                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1703                                        && !appData.icsActive));
1704         count = 0;
1705         while (p && *p++ == dmEnables[i].piece) count++;
1706         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
1707         j = 0;
1708         XtSetArg(args[j], XtNlabel, label); j++;
1709         XtSetValues(entry, args, j);
1710     }
1711 }
1712
1713 static void
1714 do_flash_delay (unsigned long msec)
1715 {
1716     TimeDelay(msec);
1717 }
1718
1719 void
1720 FlashDelay (int flash_delay)
1721 {
1722         XSync(xDisplay, False);
1723         if(flash_delay) do_flash_delay(flash_delay);
1724 }
1725
1726 double
1727 Fraction (int x, int start, int stop)
1728 {
1729    double f = ((double) x - start)/(stop - start);
1730    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1731    return f;
1732 }
1733
1734 static WindowPlacement wpNew;
1735
1736 void
1737 CoDrag (Widget sh, WindowPlacement *wp)
1738 {
1739     Arg args[16];
1740     int j=0, touch=0, fudge = 2;
1741     GetActualPlacement(sh, wp);
1742     if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x)         < fudge) touch = 1; else // right touch
1743     if(abs(wp->x + wp->width + 2*frameX - wpMain.x)            < fudge) touch = 2; else // left touch
1744     if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1745     if(abs(wp->y + wp->height + frameX + frameY - wpMain.y)    < fudge) touch = 4;      // top touch
1746     if(!touch ) return; // only windows that touch co-move
1747     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1748         int heightInc = wpNew.height - wpMain.height;
1749         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1750         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1751         wp->y += fracTop * heightInc;
1752         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1753         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1754     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1755         int widthInc = wpNew.width - wpMain.width;
1756         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1757         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1758         wp->y += fracLeft * widthInc;
1759         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1760         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1761     }
1762     wp->x += wpNew.x - wpMain.x;
1763     wp->y += wpNew.y - wpMain.y;
1764     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1765     if(touch == 3) wp->y += wpNew.height - wpMain.height;
1766     XtSetArg(args[j], XtNx, wp->x); j++;
1767     XtSetArg(args[j], XtNy, wp->y); j++;
1768     XtSetValues(sh, args, j);
1769 }
1770
1771 void
1772 ReSize (WindowPlacement *wp)
1773 {
1774         int sqx, sqy, w, h;
1775         if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1776         sqx = (wp->width  - lineGap - marginW) / BOARD_WIDTH - lineGap;
1777         sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1778         if(sqy < sqx) sqx = sqy;
1779         if(sqx != squareSize) {
1780             squareSize = sqx; // adopt new square size
1781             CreatePNGPieces(); // make newly scaled pieces
1782             InitDrawingSizes(0, 0); // creates grid etc.
1783         } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1784         w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1785         h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1786         if(optList[W_BOARD].max   > w) optList[W_BOARD].max = w;
1787         if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1788 }
1789
1790 static XtIntervalId delayedDragID = 0;
1791
1792 void
1793 DragProc ()
1794 {
1795         static int busy;
1796         if(busy) return;
1797
1798         busy = 1;
1799         GetActualPlacement(shellWidget, &wpNew);
1800         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1801            wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1802             busy = 0; return; // false alarm
1803         }
1804         ReSize(&wpNew);
1805         if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1806         if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1807         if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1808         if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1809         wpMain = wpNew;
1810         DrawPosition(True, NULL);
1811         delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1812         busy = 0;
1813 }
1814
1815
1816 void
1817 DelayedDrag ()
1818 {
1819     if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1820     delayedDragID =
1821       XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1822 }
1823
1824 void
1825 EventProc (Widget widget, caddr_t unused, XEvent *event)
1826 {
1827     if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1828         DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1829 }
1830
1831 /*
1832  * event handler for redrawing the board
1833  */
1834 void
1835 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1836 {
1837     DrawPosition(True, NULL);
1838 }
1839
1840
1841 void
1842 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1843 {   // [HGM] pv: walk PV
1844     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1845 }
1846
1847 extern int savedIndex;  /* gross that this is global */
1848
1849 void
1850 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1851 {
1852         String val;
1853         XawTextPosition index, dummy;
1854         Arg arg;
1855
1856         XawTextGetSelectionPos(w, &index, &dummy);
1857         XtSetArg(arg, XtNstring, &val);
1858         XtGetValues(w, &arg, 1);
1859         ReplaceComment(savedIndex, val);
1860         if(savedIndex != currentMove) ToNrEvent(savedIndex);
1861         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1862 }
1863
1864
1865 /* Disable all user input other than deleting the window */
1866 static int frozen = 0;
1867
1868 void
1869 FreezeUI ()
1870 {
1871   if (frozen) return;
1872   /* Grab by a widget that doesn't accept input */
1873   XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1874   frozen = 1;
1875 }
1876
1877 /* Undo a FreezeUI */
1878 void
1879 ThawUI ()
1880 {
1881   if (!frozen) return;
1882   XtRemoveGrab(optList[W_MESSG].handle);
1883   frozen = 0;
1884 }
1885
1886 void
1887 ModeHighlight ()
1888 {
1889     Arg args[16];
1890     static int oldPausing = FALSE;
1891     static GameMode oldmode = (GameMode) -1;
1892     char *wname;
1893
1894     if (!boardWidget || !XtIsRealized(boardWidget)) return;
1895
1896     if (pausing != oldPausing) {
1897         oldPausing = pausing;
1898         MarkMenuItem("Mode.Pause", pausing);
1899
1900         if (appData.showButtonBar) {
1901           /* Always toggle, don't set.  Previous code messes up when
1902              invoked while the button is pressed, as releasing it
1903              toggles the state again. */
1904           {
1905             Pixel oldbg, oldfg;
1906             XtSetArg(args[0], XtNbackground, &oldbg);
1907             XtSetArg(args[1], XtNforeground, &oldfg);
1908             XtGetValues(optList[W_PAUSE].handle,
1909                         args, 2);
1910             XtSetArg(args[0], XtNbackground, oldfg);
1911             XtSetArg(args[1], XtNforeground, oldbg);
1912           }
1913           XtSetValues(optList[W_PAUSE].handle, args, 2);
1914         }
1915     }
1916
1917     wname = ModeToWidgetName(oldmode);
1918     if (wname != NULL) {
1919         MarkMenuItem(wname, False);
1920     }
1921     wname = ModeToWidgetName(gameMode);
1922     if (wname != NULL) {
1923         MarkMenuItem(wname, True);
1924     }
1925     oldmode = gameMode;
1926     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1927
1928     /* Maybe all the enables should be handled here, not just this one */
1929     EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1930
1931     DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1932 }
1933
1934
1935 /*
1936  * Button/menu procedures
1937  */
1938
1939 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1940 char *selected_fen_position=NULL;
1941
1942 Boolean
1943 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1944                        Atom *type_return, XtPointer *value_return,
1945                        unsigned long *length_return, int *format_return)
1946 {
1947   char *selection_tmp;
1948
1949 //  if (!selected_fen_position) return False; /* should never happen */
1950   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1951    if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1952     FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1953     long len;
1954     size_t count;
1955     if (f == NULL) return False;
1956     fseek(f, 0, 2);
1957     len = ftell(f);
1958     rewind(f);
1959     selection_tmp = XtMalloc(len + 1);
1960     count = fread(selection_tmp, 1, len, f);
1961     fclose(f);
1962     if (len != count) {
1963       XtFree(selection_tmp);
1964       return False;
1965     }
1966     selection_tmp[len] = NULLCHAR;
1967    } else {
1968     /* note: since no XtSelectionDoneProc was registered, Xt will
1969      * automatically call XtFree on the value returned.  So have to
1970      * make a copy of it allocated with XtMalloc */
1971     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1972     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1973    }
1974
1975     *value_return=selection_tmp;
1976     *length_return=strlen(selection_tmp);
1977     *type_return=*target;
1978     *format_return = 8; /* bits per byte */
1979     return True;
1980   } else if (*target == XA_TARGETS(xDisplay)) {
1981     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1982     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1983     targets_tmp[1] = XA_STRING;
1984     *value_return = targets_tmp;
1985     *type_return = XA_ATOM;
1986     *length_return = 2;
1987 #if 0
1988     // This code leads to a read of value_return out of bounds on 64-bit systems.
1989     // Other code which I have seen always sets *format_return to 32 independent of
1990     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1991     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1992     *format_return = 8 * sizeof(Atom);
1993     if (*format_return > 32) {
1994       *length_return *= *format_return / 32;
1995       *format_return = 32;
1996     }
1997 #else
1998     *format_return = 32;
1999 #endif
2000     return True;
2001   } else {
2002     return False;
2003   }
2004 }
2005
2006 /* note: when called from menu all parameters are NULL, so no clue what the
2007  * Widget which was clicked on was, or what the click event was
2008  */
2009 void
2010 CopySomething (char *src)
2011 {
2012     selected_fen_position = src;
2013     /*
2014      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2015      * have a notion of a position that is selected but not copied.
2016      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2017      */
2018     XtOwnSelection(menuBarWidget, XA_PRIMARY,
2019                    CurrentTime,
2020                    SendPositionSelection,
2021                    NULL/* lose_ownership_proc */ ,
2022                    NULL/* transfer_done_proc */);
2023     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2024                    CurrentTime,
2025                    SendPositionSelection,
2026                    NULL/* lose_ownership_proc */ ,
2027                    NULL/* transfer_done_proc */);
2028 }
2029
2030 /* function called when the data to Paste is ready */
2031 static void
2032 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2033                  Atom *type, XtPointer value, unsigned long *len, int *format)
2034 {
2035   char *fenstr=value;
2036   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2037   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2038   EditPositionPasteFEN(fenstr);
2039   XtFree(value);
2040 }
2041
2042 /* called when Paste Position button is pressed,
2043  * all parameters will be NULL */
2044 void
2045 PastePositionProc ()
2046 {
2047     XtGetSelectionValue(menuBarWidget,
2048       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2049       /* (XtSelectionCallbackProc) */ PastePositionCB,
2050       NULL, /* client_data passed to PastePositionCB */
2051
2052       /* better to use the time field from the event that triggered the
2053        * call to this function, but that isn't trivial to get
2054        */
2055       CurrentTime
2056     );
2057     return;
2058 }
2059
2060 /* note: when called from menu all parameters are NULL, so no clue what the
2061  * Widget which was clicked on was, or what the click event was
2062  */
2063 /* function called when the data to Paste is ready */
2064 static void
2065 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2066              Atom *type, XtPointer value, unsigned long *len, int *format)
2067 {
2068   FILE* f;
2069   if (value == NULL || *len == 0) {
2070     return; /* nothing had been selected to copy */
2071   }
2072   f = fopen(gamePasteFilename, "w");
2073   if (f == NULL) {
2074     DisplayError(_("Can't open temp file"), errno);
2075     return;
2076   }
2077   fwrite(value, 1, *len, f);
2078   fclose(f);
2079   XtFree(value);
2080   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2081 }
2082
2083 /* called when Paste Game button is pressed,
2084  * all parameters will be NULL */
2085 void
2086 PasteGameProc ()
2087 {
2088     XtGetSelectionValue(menuBarWidget,
2089       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2090       /* (XtSelectionCallbackProc) */ PasteGameCB,
2091       NULL, /* client_data passed to PasteGameCB */
2092
2093       /* better to use the time field from the event that triggered the
2094        * call to this function, but that isn't trivial to get
2095        */
2096       CurrentTime
2097     );
2098     return;
2099 }
2100
2101
2102 void
2103 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2104 {
2105     QuitProc();
2106 }
2107
2108 int
2109 ShiftKeys ()
2110 {   // bassic primitive for determining if modifier keys are pressed
2111     long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2112     char keys[32];
2113     int i,j,  k=0;
2114     XQueryKeymap(xDisplay,keys);
2115     for(i=0; i<6; i++) {
2116         k <<= 1;
2117         j = XKeysymToKeycode(xDisplay, codes[i]);
2118         k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2119     }
2120     return k;
2121 }
2122
2123 static void
2124 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2125 {
2126     char buf[10];
2127     KeySym sym;
2128     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2129     if ( n == 1 && *buf >= 32 // printable
2130          && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2131         ) BoxAutoPopUp (buf);
2132 }
2133
2134 static void
2135 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2136 {   // [HGM] input: let up-arrow recall previous line from history
2137     IcsKey(1);
2138 }
2139
2140 static void
2141 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2142 {   // [HGM] input: let down-arrow recall next line from history
2143     IcsKey(-1);
2144 }
2145
2146 static void
2147 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2148 {
2149     IcsKey(0);
2150 }
2151
2152 void
2153 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2154 {
2155         if (!TempBackwardActive) {
2156                 TempBackwardActive = True;
2157                 BackwardEvent();
2158         }
2159 }
2160
2161 void
2162 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2163 {
2164         /* Check to see if triggered by a key release event for a repeating key.
2165          * If so the next queued event will be a key press of the same key at the same time */
2166         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2167                 XEvent next;
2168                 XPeekEvent(xDisplay, &next);
2169                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2170                         next.xkey.keycode == event->xkey.keycode)
2171                                 return;
2172         }
2173     ForwardEvent();
2174         TempBackwardActive = False;
2175 }
2176
2177 void
2178 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2179 {   // called as key binding
2180     char buf[MSG_SIZ];
2181     String name;
2182     if (nprms && *nprms > 0)
2183       name = prms[0];
2184     else
2185       name = "xboard";
2186     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2187     system(buf);
2188 }
2189
2190 void
2191 ManProc ()
2192 {   // called from menu
2193     ManInner(NULL, NULL, NULL, NULL);
2194 }
2195
2196 void
2197 SetWindowTitle (char *text, char *title, char *icon)
2198 {
2199     Arg args[16];
2200     int i;
2201     if (appData.titleInWindow) {
2202         i = 0;
2203         XtSetArg(args[i], XtNlabel, text);   i++;
2204         XtSetValues(titleWidget, args, i);
2205     }
2206     i = 0;
2207     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
2208     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
2209     XtSetValues(shellWidget, args, i);
2210     XSync(xDisplay, False);
2211 }
2212
2213
2214 static int
2215 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2216 {
2217     return 0;
2218 }
2219
2220 void
2221 DisplayIcsInteractionTitle (String message)
2222 {
2223   if (oldICSInteractionTitle == NULL) {
2224     /* Magic to find the old window title, adapted from vim */
2225     char *wina = getenv("WINDOWID");
2226     if (wina != NULL) {
2227       Window win = (Window) atoi(wina);
2228       Window root, parent, *children;
2229       unsigned int nchildren;
2230       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2231       for (;;) {
2232         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2233         if (!XQueryTree(xDisplay, win, &root, &parent,
2234                         &children, &nchildren)) break;
2235         if (children) XFree((void *)children);
2236         if (parent == root || parent == 0) break;
2237         win = parent;
2238       }
2239       XSetErrorHandler(oldHandler);
2240     }
2241     if (oldICSInteractionTitle == NULL) {
2242       oldICSInteractionTitle = "xterm";
2243     }
2244   }
2245   printf("\033]0;%s\007", message);
2246   fflush(stdout);
2247 }
2248
2249
2250 XtIntervalId delayedEventTimerXID = 0;
2251 DelayedEventCallback delayedEventCallback = 0;
2252
2253 void
2254 FireDelayedEvent ()
2255 {
2256     delayedEventTimerXID = 0;
2257     delayedEventCallback();
2258 }
2259
2260 void
2261 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2262 {
2263     if(delayedEventTimerXID && delayedEventCallback == cb)
2264         // [HGM] alive: replace, rather than add or flush identical event
2265         XtRemoveTimeOut(delayedEventTimerXID);
2266     delayedEventCallback = cb;
2267     delayedEventTimerXID =
2268       XtAppAddTimeOut(appContext, millisec,
2269                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2270 }
2271
2272 DelayedEventCallback
2273 GetDelayedEvent ()
2274 {
2275   if (delayedEventTimerXID) {
2276     return delayedEventCallback;
2277   } else {
2278     return NULL;
2279   }
2280 }
2281
2282 void
2283 CancelDelayedEvent ()
2284 {
2285   if (delayedEventTimerXID) {
2286     XtRemoveTimeOut(delayedEventTimerXID);
2287     delayedEventTimerXID = 0;
2288   }
2289 }
2290
2291 XtIntervalId loadGameTimerXID = 0;
2292
2293 int
2294 LoadGameTimerRunning ()
2295 {
2296     return loadGameTimerXID != 0;
2297 }
2298
2299 int
2300 StopLoadGameTimer ()
2301 {
2302     if (loadGameTimerXID != 0) {
2303         XtRemoveTimeOut(loadGameTimerXID);
2304         loadGameTimerXID = 0;
2305         return TRUE;
2306     } else {
2307         return FALSE;
2308     }
2309 }
2310
2311 void
2312 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2313 {
2314     loadGameTimerXID = 0;
2315     AutoPlayGameLoop();
2316 }
2317
2318 void
2319 StartLoadGameTimer (long millisec)
2320 {
2321     loadGameTimerXID =
2322       XtAppAddTimeOut(appContext, millisec,
2323                       (XtTimerCallbackProc) LoadGameTimerCallback,
2324                       (XtPointer) 0);
2325 }
2326
2327 XtIntervalId analysisClockXID = 0;
2328
2329 void
2330 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2331 {
2332     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2333          || appData.icsEngineAnalyze) { // [DM]
2334         AnalysisPeriodicEvent(0);
2335         StartAnalysisClock();
2336     }
2337 }
2338
2339 void
2340 StartAnalysisClock ()
2341 {
2342     analysisClockXID =
2343       XtAppAddTimeOut(appContext, 2000,
2344                       (XtTimerCallbackProc) AnalysisClockCallback,
2345                       (XtPointer) 0);
2346 }
2347
2348 XtIntervalId clockTimerXID = 0;
2349
2350 int
2351 ClockTimerRunning ()
2352 {
2353     return clockTimerXID != 0;
2354 }
2355
2356 int
2357 StopClockTimer ()
2358 {
2359     if (clockTimerXID != 0) {
2360         XtRemoveTimeOut(clockTimerXID);
2361         clockTimerXID = 0;
2362         return TRUE;
2363     } else {
2364         return FALSE;
2365     }
2366 }
2367
2368 void
2369 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2370 {
2371     clockTimerXID = 0;
2372     DecrementClocks();
2373 }
2374
2375 void
2376 StartClockTimer (long millisec)
2377 {
2378     clockTimerXID =
2379       XtAppAddTimeOut(appContext, millisec,
2380                       (XtTimerCallbackProc) ClockTimerCallback,
2381                       (XtPointer) 0);
2382 }
2383
2384 void
2385 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2386 {
2387     char buf[MSG_SIZ];
2388     Arg args[16];
2389     Widget w = (Widget) opt->handle;
2390
2391     /* check for low time warning */
2392     Pixel foregroundOrWarningColor = timerForegroundPixel;
2393
2394     if (timer > 0 &&
2395         appData.lowTimeWarning &&
2396         (timer / 1000) < appData.icsAlarmTime)
2397       foregroundOrWarningColor = lowTimeWarningColor;
2398
2399     if (appData.clockMode) {
2400       snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2401       XtSetArg(args[0], XtNlabel, buf);
2402     } else {
2403       snprintf(buf, MSG_SIZ, "%s  ", color);
2404       XtSetArg(args[0], XtNlabel, buf);
2405     }
2406
2407     if (highlight) {
2408
2409         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2410         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2411     } else {
2412         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2413         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2414     }
2415
2416     XtSetValues(w, args, 3);
2417 }
2418
2419 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2420
2421 void
2422 SetClockIcon (int color)
2423 {
2424     Arg args[16];
2425     Pixmap pm = *clockIcons[color];
2426     if (iconPixmap != pm) {
2427         iconPixmap = pm;
2428         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2429         XtSetValues(shellWidget, args, 1);
2430     }
2431 }
2432
2433 #define INPUT_SOURCE_BUF_SIZE 8192
2434
2435 typedef struct {
2436     CPKind kind;
2437     int fd;
2438     int lineByLine;
2439     char *unused;
2440     InputCallback func;
2441     XtInputId xid;
2442     char buf[INPUT_SOURCE_BUF_SIZE];
2443     VOIDSTAR closure;
2444 } InputSource;
2445
2446 void
2447 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2448 {
2449     InputSource *is = (InputSource *) closure;
2450     int count;
2451     int error;
2452     char *p, *q;
2453
2454     if (is->lineByLine) {
2455         count = read(is->fd, is->unused,
2456                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2457         if (count <= 0) {
2458             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2459             return;
2460         }
2461         is->unused += count;
2462         p = is->buf;
2463         while (p < is->unused) {
2464             q = memchr(p, '\n', is->unused - p);
2465             if (q == NULL) break;
2466             q++;
2467             (is->func)(is, is->closure, p, q - p, 0);
2468             p = q;
2469         }
2470         q = is->buf;
2471         while (p < is->unused) {
2472             *q++ = *p++;
2473         }
2474         is->unused = q;
2475     } else {
2476         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2477         if (count == -1)
2478           error = errno;
2479         else
2480           error = 0;
2481         (is->func)(is, is->closure, is->buf, count, error);
2482     }
2483 }
2484
2485 InputSourceRef
2486 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2487 {
2488     InputSource *is;
2489     ChildProc *cp = (ChildProc *) pr;
2490
2491     is = (InputSource *) calloc(1, sizeof(InputSource));
2492     is->lineByLine = lineByLine;
2493     is->func = func;
2494     if (pr == NoProc) {
2495         is->kind = CPReal;
2496         is->fd = fileno(stdin);
2497     } else {
2498         is->kind = cp->kind;
2499         is->fd = cp->fdFrom;
2500     }
2501     if (lineByLine) {
2502         is->unused = is->buf;
2503     }
2504
2505     is->xid = XtAppAddInput(appContext, is->fd,
2506                             (XtPointer) (XtInputReadMask),
2507                             (XtInputCallbackProc) DoInputCallback,
2508                             (XtPointer) is);
2509     is->closure = closure;
2510     return (InputSourceRef) is;
2511 }
2512
2513 void
2514 RemoveInputSource (InputSourceRef isr)
2515 {
2516     InputSource *is = (InputSource *) isr;
2517
2518     if (is->xid == 0) return;
2519     XtRemoveInput(is->xid);
2520     is->xid = 0;
2521 }
2522
2523 #ifndef HAVE_USLEEP
2524
2525 static Boolean frameWaiting;
2526
2527 static RETSIGTYPE
2528 FrameAlarm (int sig)
2529 {
2530   frameWaiting = False;
2531   /* In case System-V style signals.  Needed?? */
2532   signal(SIGALRM, FrameAlarm);
2533 }
2534
2535 void
2536 FrameDelay (int time)
2537 {
2538   struct itimerval delay;
2539
2540   XSync(xDisplay, False);
2541
2542   if (time > 0) {
2543     frameWaiting = True;
2544     signal(SIGALRM, FrameAlarm);
2545     delay.it_interval.tv_sec =
2546       delay.it_value.tv_sec = time / 1000;
2547     delay.it_interval.tv_usec =
2548       delay.it_value.tv_usec = (time % 1000) * 1000;
2549     setitimer(ITIMER_REAL, &delay, NULL);
2550     while (frameWaiting) pause();
2551     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2552     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2553     setitimer(ITIMER_REAL, &delay, NULL);
2554   }
2555 }
2556
2557 #else
2558
2559 void
2560 FrameDelay (int time)
2561 {
2562   XSync(xDisplay, False);
2563   if (time > 0)
2564     usleep(time * 1000);
2565 }
2566
2567 #endif
2568
2569 static void
2570 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2571 {
2572     char buf[MSG_SIZ], *logoName = buf;
2573     if(appData.logo[n][0]) {
2574         logoName = appData.logo[n];
2575     } else if(appData.autoLogo) {
2576         if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2577             sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2578         } else if(appData.directory[n] && appData.directory[n][0]) {
2579             sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2580         }
2581     }
2582     if(logoName[0])
2583         { ASSIGN(cps->programLogo, logoName); }
2584 }
2585
2586 void
2587 UpdateLogos (int displ)
2588 {
2589     if(optList[W_WHITE-1].handle == NULL) return;
2590     LoadLogo(&first, 0, 0);
2591     LoadLogo(&second, 1, appData.icsActive);
2592     if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2593     return;
2594 }
2595