updated copyright for 2016
[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, 2016 Free
9  * Software Foundation, Inc.
10  *
11  * The following terms apply to Digital Equipment Corporation's copyright
12  * interest in XBoard:
13  * ------------------------------------------------------------------------
14  * All Rights Reserved
15  *
16  * Permission to use, copy, modify, and distribute this software and its
17  * documentation for any purpose and without fee is hereby granted,
18  * provided that the above copyright notice appear in all copies and that
19  * both that copyright notice and this permission notice appear in
20  * supporting documentation, and that the name of Digital not be
21  * used in advertising or publicity pertaining to distribution of the
22  * software without specific, written prior permission.
23  *
24  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
26  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
27  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
28  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
29  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30  * SOFTWARE.
31  * ------------------------------------------------------------------------
32  *
33  * The following terms apply to the enhanced version of XBoard
34  * distributed by the Free Software Foundation:
35  * ------------------------------------------------------------------------
36  *
37  * GNU XBoard is free software: you can redistribute it and/or modify
38  * it under the terms of the GNU General Public License as published by
39  * the Free Software Foundation, either version 3 of the License, or (at
40  * your option) any later version.
41  *
42  * GNU XBoard is distributed in the hope that it will be useful, but
43  * WITHOUT ANY WARRANTY; without even the implied warranty of
44  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
45  * General Public License for more details.
46  *
47  * You should have received a copy of the GNU General Public License
48  * along with this program. If not, see http://www.gnu.org/licenses/.  *
49  *
50  *------------------------------------------------------------------------
51  ** See the file ChangeLog for a revision history.  */
52
53 #define HIGHDRAG 1
54
55 #include "config.h"
56
57 #include <stdio.h>
58 #include <ctype.h>
59 #include <signal.h>
60 #include <errno.h>
61 #include <sys/types.h>
62 #include <sys/stat.h>
63 #include <pwd.h>
64 #include <math.h>
65 #include <cairo/cairo.h>
66 #include <cairo/cairo-xlib.h>
67
68 #if !OMIT_SOCKETS
69 # if HAVE_SYS_SOCKET_H
70 #  include <sys/socket.h>
71 #  include <netinet/in.h>
72 #  include <netdb.h>
73 # else /* not HAVE_SYS_SOCKET_H */
74 #  if HAVE_LAN_SOCKET_H
75 #   include <lan/socket.h>
76 #   include <lan/in.h>
77 #   include <lan/netdb.h>
78 #  else /* not HAVE_LAN_SOCKET_H */
79 #   define OMIT_SOCKETS 1
80 #  endif /* not HAVE_LAN_SOCKET_H */
81 # endif /* not HAVE_SYS_SOCKET_H */
82 #endif /* !OMIT_SOCKETS */
83
84 #if STDC_HEADERS
85 # include <stdlib.h>
86 # include <string.h>
87 #else /* not STDC_HEADERS */
88 extern char *getenv();
89 # if HAVE_STRING_H
90 #  include <string.h>
91 # else /* not HAVE_STRING_H */
92 #  include <strings.h>
93 # endif /* not HAVE_STRING_H */
94 #endif /* not STDC_HEADERS */
95
96 #if HAVE_SYS_FCNTL_H
97 # include <sys/fcntl.h>
98 #else /* not HAVE_SYS_FCNTL_H */
99 # if HAVE_FCNTL_H
100 #  include <fcntl.h>
101 # endif /* HAVE_FCNTL_H */
102 #endif /* not HAVE_SYS_FCNTL_H */
103
104 #if HAVE_SYS_SYSTEMINFO_H
105 # include <sys/systeminfo.h>
106 #endif /* HAVE_SYS_SYSTEMINFO_H */
107
108 #if TIME_WITH_SYS_TIME
109 # include <sys/time.h>
110 # include <time.h>
111 #else
112 # if HAVE_SYS_TIME_H
113 #  include <sys/time.h>
114 # else
115 #  include <time.h>
116 # endif
117 #endif
118
119 #if HAVE_UNISTD_H
120 # include <unistd.h>
121 #endif
122
123 #if HAVE_SYS_WAIT_H
124 # include <sys/wait.h>
125 #endif
126
127 #if HAVE_DIRENT_H
128 # include <dirent.h>
129 # define NAMLEN(dirent) strlen((dirent)->d_name)
130 # define HAVE_DIR_STRUCT
131 #else
132 # define dirent direct
133 # define NAMLEN(dirent) (dirent)->d_namlen
134 # if HAVE_SYS_NDIR_H
135 #  include <sys/ndir.h>
136 #  define HAVE_DIR_STRUCT
137 # endif
138 # if HAVE_SYS_DIR_H
139 #  include <sys/dir.h>
140 #  define HAVE_DIR_STRUCT
141 # endif
142 # if HAVE_NDIR_H
143 #  include <ndir.h>
144 #  define HAVE_DIR_STRUCT
145 # endif
146 #endif
147
148 #if ENABLE_NLS
149 #include <locale.h>
150 #endif
151
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     oldmode = gameMode;
1856     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
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