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