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