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