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