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