Implement menu checkmarking and enabling
[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     if(item) gtk_widget_set_sensitive(item->handle, state);
1698 }
1699
1700 void
1701 EnableButtonBar (int state)
1702 {
1703 #ifdef TODO_GTK
1704     XtSetSensitive(optList[W_BUTTON].handle, state);
1705 #endif
1706 }
1707
1708
1709 void
1710 SetMenuEnables (Enables *enab)
1711 {
1712   while (enab->name != NULL) {
1713     EnableNamedMenuItem(enab->name, enab->value);
1714     enab++;
1715   }
1716 }
1717
1718 #ifdef TODO_GTK
1719 void
1720 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1721 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1722     MenuItem *item;
1723     if(*nprms == 0) return;
1724     item = MenuNameToItem(prms[0]);
1725     if(item) ((MenuProc *) item->proc) ();
1726 }
1727 #endif
1728
1729 #ifdef TODO_GTK
1730 static void
1731 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
1732 {
1733     RecentEngineEvent((int) (intptr_t) addr);
1734 }
1735 #endif
1736
1737 void
1738 AppendMenuItem (char *msg, int n)
1739 {
1740 #ifdef TODO_GTK
1741     CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
1742 #endif
1743 }
1744
1745 void
1746 SetupDropMenu ()
1747 {
1748 #ifdef TODO_GTK
1749     int i, j, count;
1750     char label[32];
1751     Arg args[16];
1752     Widget entry;
1753     char* p;
1754
1755     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1756         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1757         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1758                    dmEnables[i].piece);
1759         XtSetSensitive(entry, p != NULL || !appData.testLegality
1760                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1761                                        && !appData.icsActive));
1762         count = 0;
1763         while (p && *p++ == dmEnables[i].piece) count++;
1764         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
1765         j = 0;
1766         XtSetArg(args[j], XtNlabel, label); j++;
1767         XtSetValues(entry, args, j);
1768     }
1769 #endif
1770 }
1771
1772 static void
1773 do_flash_delay (unsigned long msec)
1774 {
1775     TimeDelay(msec);
1776 }
1777
1778 void
1779 FlashDelay (int flash_delay)
1780 {
1781 #ifdef TODO_GTK
1782         XSync(xDisplay, False);
1783         if(flash_delay) do_flash_delay(flash_delay);
1784 #endif
1785 }
1786
1787 double
1788 Fraction (int x, int start, int stop)
1789 {
1790    double f = ((double) x - start)/(stop - start);
1791    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1792    return f;
1793 }
1794
1795 static WindowPlacement wpNew;
1796
1797 #ifdef TODO_GTK
1798 void
1799 CoDrag (Widget sh, WindowPlacement *wp)
1800 {
1801     Arg args[16];
1802     int j=0, touch=0, fudge = 2;
1803     GetActualPlacement(sh, wp);
1804     if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x)         < fudge) touch = 1; else // right touch
1805     if(abs(wp->x + wp->width + 2*frameX - wpMain.x)            < fudge) touch = 2; else // left touch
1806     if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1807     if(abs(wp->y + wp->height + frameX + frameY - wpMain.y)    < fudge) touch = 4;      // top touch
1808     if(!touch ) return; // only windows that touch co-move
1809     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1810         int heightInc = wpNew.height - wpMain.height;
1811         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1812         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1813         wp->y += fracTop * heightInc;
1814         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1815         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1816     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1817         int widthInc = wpNew.width - wpMain.width;
1818         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1819         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1820         wp->y += fracLeft * widthInc;
1821         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1822         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1823     }
1824     wp->x += wpNew.x - wpMain.x;
1825     wp->y += wpNew.y - wpMain.y;
1826     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1827     if(touch == 3) wp->y += wpNew.height - wpMain.height;
1828 #ifdef TODO_GTK
1829     XtSetArg(args[j], XtNx, wp->x); j++;
1830     XtSetArg(args[j], XtNy, wp->y); j++;
1831     XtSetValues(sh, args, j);
1832 #endif
1833 }
1834
1835 void
1836 ReSize (WindowPlacement *wp)
1837 {
1838         int sqx, sqy, w, h;
1839         if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1840         sqx = (wp->width  - lineGap - marginW) / BOARD_WIDTH - lineGap;
1841         sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1842         if(sqy < sqx) sqx = sqy;
1843         if(sqx != squareSize) {
1844             squareSize = sqx; // adopt new square size
1845             CreatePNGPieces(); // make newly scaled pieces
1846             InitDrawingSizes(0, 0); // creates grid etc.
1847         } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1848         w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1849         h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1850         if(optList[W_BOARD].max   > w) optList[W_BOARD].max = w;
1851         if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1852 }
1853
1854 #ifdef TODO_GTK
1855 static XtIntervalId delayedDragID = 0;
1856 #else
1857 static int delayedDragID = 0;
1858 #endif
1859
1860 void
1861 DragProc ()
1862 {
1863         static int busy;
1864         if(busy) return;
1865
1866         busy = 1;
1867         GetActualPlacement(shellWidget, &wpNew);
1868         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1869            wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1870             busy = 0; return; // false alarm
1871         }
1872         ReSize(&wpNew);
1873         if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1874         if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1875         if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1876         if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1877         wpMain = wpNew;
1878         DrawPosition(True, NULL);
1879         delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1880         busy = 0;
1881 }
1882 #endif
1883
1884 void
1885 DelayedDrag ()
1886 {
1887 #ifdef TODO_GTK
1888     if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1889     delayedDragID =
1890       XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1891 #endif
1892 }
1893
1894 void
1895 EventProc (Widget widget, caddr_t unused, XEvent *event)
1896 {
1897 #ifdef TODO_GTK
1898     if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1899         DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1900 #endif
1901 }
1902
1903 /*
1904  * event handler for redrawing the board
1905  */
1906 void
1907 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1908 {
1909     DrawPosition(True, NULL);
1910 }
1911
1912
1913 #ifdef TODO_GTK
1914 void
1915 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1916 {   // [HGM] pv: walk PV
1917     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1918 }
1919 #endif
1920
1921 static int savedIndex;  /* gross that this is global */
1922
1923 void
1924 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1925 {
1926 #ifdef TODO_GTK
1927         String val;
1928         XawTextPosition index, dummy;
1929         Arg arg;
1930
1931         XawTextGetSelectionPos(w, &index, &dummy);
1932         XtSetArg(arg, XtNstring, &val);
1933         XtGetValues(w, &arg, 1);
1934         ReplaceComment(savedIndex, val);
1935         if(savedIndex != currentMove) ToNrEvent(savedIndex);
1936         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1937 #endif
1938 }
1939
1940 void
1941 EditCommentPopUp (int index, char *title, char *text)
1942 {
1943     savedIndex = index;
1944     if (text == NULL) text = "";
1945     NewCommentPopup(title, text, index);
1946 }
1947
1948 void
1949 CommentPopUp (char *title, char *text)
1950 {
1951     savedIndex = currentMove; // [HGM] vari
1952     NewCommentPopup(title, text, currentMove);
1953 }
1954
1955 void
1956 CommentPopDown ()
1957 {
1958     PopDown(CommentDlg);
1959 }
1960
1961
1962 /* Disable all user input other than deleting the window */
1963 static int frozen = 0;
1964
1965 void
1966 FreezeUI ()
1967 {
1968   if (frozen) return;
1969   /* Grab by a widget that doesn't accept input */
1970   XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1971   frozen = 1;
1972 }
1973
1974 /* Undo a FreezeUI */
1975 void
1976 ThawUI ()
1977 {
1978   if (!frozen) return;
1979 #ifdef TODO_GTK
1980   XtRemoveGrab(optList[W_MESSG].handle);
1981 #endif
1982   frozen = 0;
1983 }
1984
1985 void
1986 ModeHighlight ()
1987 {
1988     static int oldPausing = FALSE;
1989     static GameMode oldmode = (GameMode) -1;
1990     char *wname;
1991 #ifdef TODO_GTK
1992     Arg args[16];
1993
1994     if (!boardWidget || !XtIsRealized(boardWidget)) return;
1995
1996     if (pausing != oldPausing) {
1997         oldPausing = pausing;
1998         MarkMenuItem("Mode.Pause", pausing);
1999
2000         if (appData.showButtonBar) {
2001           /* Always toggle, don't set.  Previous code messes up when
2002              invoked while the button is pressed, as releasing it
2003              toggles the state again. */
2004           {
2005             Pixel oldbg, oldfg;
2006             XtSetArg(args[0], XtNbackground, &oldbg);
2007             XtSetArg(args[1], XtNforeground, &oldfg);
2008             XtGetValues(optList[W_PAUSE].handle,
2009                         args, 2);
2010             XtSetArg(args[0], XtNbackground, oldfg);
2011             XtSetArg(args[1], XtNforeground, oldbg);
2012           }
2013           XtSetValues(optList[W_PAUSE].handle, args, 2);
2014         }
2015     }
2016 #endif
2017
2018     wname = ModeToWidgetName(oldmode);
2019     if (wname != NULL) {
2020         MarkMenuItem(wname, False);
2021     }
2022     wname = ModeToWidgetName(gameMode);
2023     if (wname != NULL) {
2024         MarkMenuItem(wname, True);
2025     }
2026     oldmode = gameMode;
2027     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
2028
2029     /* Maybe all the enables should be handled here, not just this one */
2030     EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
2031
2032     DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2033 }
2034
2035
2036 /*
2037  * Button/menu procedures
2038  */
2039
2040 #ifdef TODO_GTK
2041 /* this variable is shared between CopyPositionProc and SendPositionSelection */
2042 char *selected_fen_position=NULL;
2043
2044 Boolean
2045 SendPositionSelection (Widget w, Atom *selection, Atom *target,
2046                        Atom *type_return, XtPointer *value_return,
2047                        unsigned long *length_return, int *format_return)
2048 {
2049   char *selection_tmp;
2050
2051 //  if (!selected_fen_position) return False; /* should never happen */
2052   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
2053    if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
2054     FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
2055     long len;
2056     size_t count;
2057     if (f == NULL) return False;
2058     fseek(f, 0, 2);
2059     len = ftell(f);
2060     rewind(f);
2061     selection_tmp = XtMalloc(len + 1);
2062     count = fread(selection_tmp, 1, len, f);
2063     fclose(f);
2064     if (len != count) {
2065       XtFree(selection_tmp);
2066       return False;
2067     }
2068     selection_tmp[len] = NULLCHAR;
2069    } else {
2070     /* note: since no XtSelectionDoneProc was registered, Xt will
2071      * automatically call XtFree on the value returned.  So have to
2072      * make a copy of it allocated with XtMalloc */
2073     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
2074     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
2075    }
2076
2077     *value_return=selection_tmp;
2078     *length_return=strlen(selection_tmp);
2079     *type_return=*target;
2080     *format_return = 8; /* bits per byte */
2081     return True;
2082   } else if (*target == XA_TARGETS(xDisplay)) {
2083     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
2084     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
2085     targets_tmp[1] = XA_STRING;
2086     *value_return = targets_tmp;
2087     *type_return = XA_ATOM;
2088     *length_return = 2;
2089 #if 0
2090     // This code leads to a read of value_return out of bounds on 64-bit systems.
2091     // Other code which I have seen always sets *format_return to 32 independent of
2092     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
2093     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
2094     *format_return = 8 * sizeof(Atom);
2095     if (*format_return > 32) {
2096       *length_return *= *format_return / 32;
2097       *format_return = 32;
2098     }
2099 #else
2100     *format_return = 32;
2101 #endif
2102     return True;
2103   } else {
2104     return False;
2105   }
2106 }
2107 #endif
2108
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 void
2113 CopySomething (char *src)
2114 {
2115 #ifdef TODO_GTK
2116     selected_fen_position = src;
2117     /*
2118      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2119      * have a notion of a position that is selected but not copied.
2120      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2121      */
2122     XtOwnSelection(menuBarWidget, XA_PRIMARY,
2123                    CurrentTime,
2124                    SendPositionSelection,
2125                    NULL/* lose_ownership_proc */ ,
2126                    NULL/* transfer_done_proc */);
2127     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2128                    CurrentTime,
2129                    SendPositionSelection,
2130                    NULL/* lose_ownership_proc */ ,
2131                    NULL/* transfer_done_proc */);
2132 #endif
2133 }
2134
2135 #ifdef TODO_GTK
2136 /* function called when the data to Paste is ready */
2137 static void
2138 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2139                  Atom *type, XtPointer value, unsigned long *len, int *format)
2140 {
2141   char *fenstr=value;
2142   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2143   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2144   EditPositionPasteFEN(fenstr);
2145   XtFree(value);
2146 }
2147 #endif
2148
2149 /* called when Paste Position button is pressed,
2150  * all parameters will be NULL */
2151 void
2152 PastePositionProc ()
2153 {
2154 #ifdef TODO_GTK
2155     XtGetSelectionValue(menuBarWidget,
2156       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2157       /* (XtSelectionCallbackProc) */ PastePositionCB,
2158       NULL, /* client_data passed to PastePositionCB */
2159
2160       /* better to use the time field from the event that triggered the
2161        * call to this function, but that isn't trivial to get
2162        */
2163       CurrentTime
2164     );
2165     return;
2166 #endif
2167 }
2168
2169 #ifdef TODO_GTK
2170 /* note: when called from menu all parameters are NULL, so no clue what the
2171  * Widget which was clicked on was, or what the click event was
2172  */
2173 /* function called when the data to Paste is ready */
2174 static void
2175 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2176              Atom *type, XtPointer value, unsigned long *len, int *format)
2177 {
2178   FILE* f;
2179   if (value == NULL || *len == 0) {
2180     return; /* nothing had been selected to copy */
2181   }
2182   f = fopen(gamePasteFilename, "w");
2183   if (f == NULL) {
2184     DisplayError(_("Can't open temp file"), errno);
2185     return;
2186   }
2187   fwrite(value, 1, *len, f);
2188   fclose(f);
2189   XtFree(value);
2190   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2191 }
2192 #endif
2193
2194 /* called when Paste Game button is pressed,
2195  * all parameters will be NULL */
2196 void
2197 PasteGameProc ()
2198 {
2199 #ifdef TODO_GTK
2200     XtGetSelectionValue(menuBarWidget,
2201       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2202       /* (XtSelectionCallbackProc) */ PasteGameCB,
2203       NULL, /* client_data passed to PasteGameCB */
2204
2205       /* better to use the time field from the event that triggered the
2206        * call to this function, but that isn't trivial to get
2207        */
2208       CurrentTime
2209     );
2210     return;
2211 #endif
2212 }
2213
2214
2215 void
2216 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2217 {
2218     QuitProc();
2219 }
2220
2221 int
2222 ShiftKeys ()
2223 {   // bassic primitive for determining if modifier keys are pressed
2224     int i,j,  k=0;
2225 #ifdef TODO_GTK
2226     long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2227     char keys[32];
2228     XQueryKeymap(xDisplay,keys);
2229     for(i=0; i<6; i++) {
2230         k <<= 1;
2231         j = XKeysymToKeycode(xDisplay, codes[i]);
2232         k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2233     }
2234 #endif
2235     return k;
2236 }
2237
2238 static void
2239 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2240 {
2241 #ifdef TODO_GTK
2242     char buf[10];
2243     KeySym sym;
2244     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2245     if ( n == 1 && *buf >= 32 // printable
2246          && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2247         ) BoxAutoPopUp (buf);
2248 #endif
2249 }
2250
2251 #ifdef TODO_GTK
2252 static void
2253 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2254 {   // [HGM] input: let up-arrow recall previous line from history
2255     IcsKey(1);
2256 }
2257
2258 static void
2259 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2260 {   // [HGM] input: let down-arrow recall next line from history
2261     IcsKey(-1);
2262 }
2263
2264 static void
2265 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2266 {
2267     IcsKey(0);
2268 }
2269
2270
2271 void
2272 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2273 {
2274         if (!TempBackwardActive) {
2275                 TempBackwardActive = True;
2276                 BackwardEvent();
2277         }
2278 }
2279
2280 void
2281 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2282 {
2283         /* Check to see if triggered by a key release event for a repeating key.
2284          * If so the next queued event will be a key press of the same key at the same time */
2285         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2286                 XEvent next;
2287                 XPeekEvent(xDisplay, &next);
2288                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2289                         next.xkey.keycode == event->xkey.keycode)
2290                                 return;
2291         }
2292     ForwardEvent();
2293         TempBackwardActive = False;
2294 }
2295
2296 void
2297 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2298 {   // called as key binding
2299     char buf[MSG_SIZ];
2300     String name;
2301     if (nprms && *nprms > 0)
2302       name = prms[0];
2303     else
2304       name = "xboard";
2305     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2306     system(buf);
2307 }
2308 #endif
2309
2310 void
2311 ManProc ()
2312 {   // called from menu
2313 #ifdef TODO_GTK
2314     ManInner(NULL, NULL, NULL, NULL);
2315 #endif
2316 }
2317
2318 void
2319 SetWindowTitle (char *text, char *title, char *icon)
2320 {
2321 #ifdef TODO_GTK
2322     Arg args[16];
2323     int i;
2324     if (appData.titleInWindow) {
2325         i = 0;
2326         XtSetArg(args[i], XtNlabel, text);   i++;
2327         XtSetValues(titleWidget, args, i);
2328     }
2329     i = 0;
2330     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
2331     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
2332     XtSetValues(shellWidget, args, i);
2333     XSync(xDisplay, False);
2334 #endif
2335 }
2336
2337
2338 static int
2339 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2340 {
2341     return 0;
2342 }
2343
2344 void
2345 DisplayIcsInteractionTitle (String message)
2346 {
2347 #ifdef TODO_GTK
2348   if (oldICSInteractionTitle == NULL) {
2349     /* Magic to find the old window title, adapted from vim */
2350     char *wina = getenv("WINDOWID");
2351     if (wina != NULL) {
2352       Window win = (Window) atoi(wina);
2353       Window root, parent, *children;
2354       unsigned int nchildren;
2355       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2356       for (;;) {
2357         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2358         if (!XQueryTree(xDisplay, win, &root, &parent,
2359                         &children, &nchildren)) break;
2360         if (children) XFree((void *)children);
2361         if (parent == root || parent == 0) break;
2362         win = parent;
2363       }
2364       XSetErrorHandler(oldHandler);
2365     }
2366     if (oldICSInteractionTitle == NULL) {
2367       oldICSInteractionTitle = "xterm";
2368     }
2369   }
2370   printf("\033]0;%s\007", message);
2371   fflush(stdout);
2372 #endif
2373 }
2374
2375
2376 void
2377 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2378 {
2379 #ifdef TODO_GTK
2380     char buf[MSG_SIZ];
2381     Arg args[16];
2382     Widget w = (Widget) opt->handle;
2383
2384     /* check for low time warning */
2385     Pixel foregroundOrWarningColor = timerForegroundPixel;
2386
2387     if (timer > 0 &&
2388         appData.lowTimeWarning &&
2389         (timer / 1000) < appData.icsAlarmTime)
2390       foregroundOrWarningColor = lowTimeWarningColor;
2391
2392     if (appData.clockMode) {
2393       snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2394       XtSetArg(args[0], XtNlabel, buf);
2395     } else {
2396       snprintf(buf, MSG_SIZ, "%s  ", color);
2397       XtSetArg(args[0], XtNlabel, buf);
2398     }
2399
2400     if (highlight) {
2401
2402         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2403         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2404     } else {
2405         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2406         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2407     }
2408
2409     XtSetValues(w, args, 3);
2410 #endif
2411 }
2412
2413 #ifdef TODO_GTK
2414 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2415 #endif
2416
2417 void
2418 SetClockIcon (int color)
2419 {
2420 #ifdef TODO_GTK
2421     Arg args[16];
2422     Pixmap pm = *clockIcons[color];
2423     if (iconPixmap != pm) {
2424         iconPixmap = pm;
2425         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2426         XtSetValues(shellWidget, args, 1);
2427     }
2428 #endif
2429 }
2430
2431 #ifdef TODO_GTK
2432 void
2433 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2434 {
2435     InputSource *is = (InputSource *) closure;
2436     int count;
2437     int error;
2438     char *p, *q;
2439
2440     if (is->lineByLine) {
2441         count = read(is->fd, is->unused,
2442                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2443         if (count <= 0) {
2444             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2445             return;
2446         }
2447         is->unused += count;
2448         p = is->buf;
2449         while (p < is->unused) {
2450             q = memchr(p, '\n', is->unused - p);
2451             if (q == NULL) break;
2452             q++;
2453             (is->func)(is, is->closure, p, q - p, 0);
2454             p = q;
2455         }
2456         q = is->buf;
2457         while (p < is->unused) {
2458             *q++ = *p++;
2459         }
2460         is->unused = q;
2461     } else {
2462         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2463         if (count == -1)
2464           error = errno;
2465         else
2466           error = 0;
2467         (is->func)(is, is->closure, is->buf, count, error);
2468     }
2469 }
2470 #endif
2471
2472 InputSourceRef
2473 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2474 {
2475 #ifdef TODO_GTK
2476     InputSource *is;
2477     ChildProc *cp = (ChildProc *) pr;
2478
2479     is = (InputSource *) calloc(1, sizeof(InputSource));
2480     is->lineByLine = lineByLine;
2481     is->func = func;
2482     if (pr == NoProc) {
2483         is->kind = CPReal;
2484         is->fd = fileno(stdin);
2485     } else {
2486         is->kind = cp->kind;
2487         is->fd = cp->fdFrom;
2488     }
2489     if (lineByLine) {
2490         is->unused = is->buf;
2491     }
2492
2493     is->xid = XtAppAddInput(appContext, is->fd,
2494                             (XtPointer) (XtInputReadMask),
2495                             (XtInputCallbackProc) DoInputCallback,
2496                             (XtPointer) is);
2497     is->closure = closure;
2498     return (InputSourceRef) is;
2499 #else
2500     return (InputSourceRef) 0;
2501 #endif
2502 }
2503
2504 void
2505 RemoveInputSource (InputSourceRef isr)
2506 {
2507 #ifdef TODO_GTK
2508     InputSource *is = (InputSource *) isr;
2509
2510     if (is->xid == 0) return;
2511     XtRemoveInput(is->xid);
2512     is->xid = 0;
2513 #endif
2514 }
2515
2516 #ifndef HAVE_USLEEP
2517
2518 static Boolean frameWaiting;
2519
2520 static RETSIGTYPE
2521 FrameAlarm (int sig)
2522 {
2523   frameWaiting = False;
2524   /* In case System-V style signals.  Needed?? */
2525   signal(SIGALRM, FrameAlarm);
2526 }
2527
2528 void
2529 FrameDelay (int time)
2530 {
2531 #ifdef TODO_GTK
2532   struct itimerval delay;
2533
2534   XSync(xDisplay, False);
2535
2536   if (time > 0) {
2537     frameWaiting = True;
2538     signal(SIGALRM, FrameAlarm);
2539     delay.it_interval.tv_sec =
2540       delay.it_value.tv_sec = time / 1000;
2541     delay.it_interval.tv_usec =
2542       delay.it_value.tv_usec = (time % 1000) * 1000;
2543     setitimer(ITIMER_REAL, &delay, NULL);
2544     while (frameWaiting) pause();
2545     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2546     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2547     setitimer(ITIMER_REAL, &delay, NULL);
2548   }
2549 #endif
2550 }
2551
2552 #else
2553
2554 void
2555 FrameDelay (int time)
2556 {
2557 #ifdef TODO_GTK
2558   XSync(xDisplay, False);
2559 #endif
2560   if (time > 0)
2561     usleep(time * 1000);
2562 }
2563
2564 #endif
2565
2566 static void
2567 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2568 {
2569     char buf[MSG_SIZ], *logoName = buf;
2570     if(appData.logo[n][0]) {
2571         logoName = appData.logo[n];
2572     } else if(appData.autoLogo) {
2573         if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2574             sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2575         } else if(appData.directory[n] && appData.directory[n][0]) {
2576             sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2577         }
2578     }
2579     if(logoName[0])
2580         { ASSIGN(cps->programLogo, logoName); }
2581 }
2582
2583 void
2584 UpdateLogos (int displ)
2585 {
2586     if(optList[W_WHITE-1].handle == NULL) return;
2587     LoadLogo(&first, 0, 0);
2588     LoadLogo(&second, 1, appData.icsActive);
2589     if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2590     return;
2591 }
2592