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