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