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