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