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