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