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