4f3066eabbd6c0d4ddf26d2d82c08040a991f83f
[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     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1211     if (strlen(appData.pixmapDirectory) > 0) {
1212         p = ExpandPathName(appData.pixmapDirectory);
1213         if (!p) {
1214             fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1215                    appData.pixmapDirectory);
1216             exit(1);
1217         }
1218         if (appData.debugMode) {
1219           fprintf(stderr, _("\
1220 XBoard square size (hint): %d\n\
1221 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1222         }
1223         squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1224         if (appData.debugMode) {
1225             fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1226         }
1227     }
1228     defaultLineGap = lineGap;
1229     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1230
1231     /* [HR] height treated separately (hacked) */
1232     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1233     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1234
1235     /*
1236      * Determine what fonts to use.
1237      */
1238     InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1239
1240     /*
1241      * Detect if there are not enough colors available and adapt.
1242      */
1243     if (DefaultDepth(xDisplay, xScreen) <= 2) {
1244       appData.monoMode = True;
1245     }
1246
1247     forceMono = MakeColors();
1248
1249     if (forceMono) {
1250       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1251               programName);
1252         appData.monoMode = True;
1253     }
1254
1255     if (appData.monoMode && appData.debugMode) {
1256         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1257                 (unsigned long) XWhitePixel(xDisplay, xScreen),
1258                 (unsigned long) XBlackPixel(xDisplay, xScreen));
1259     }
1260
1261     ParseIcsTextColors();
1262
1263     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1264
1265     /*
1266      * widget hierarchy
1267      */
1268     if (tinyLayout) {
1269         layoutName = "tinyLayout";
1270     } else if (smallLayout) {
1271         layoutName = "smallLayout";
1272     } else {
1273         layoutName = "normalLayout";
1274     }
1275
1276     optList = BoardPopUp(squareSize, lineGap, (void*)
1277 #if ENABLE_NLS
1278                                                 &clockFontSet);
1279 #else
1280                                                 clockFontStruct);
1281 #endif
1282     boardWidget      = optList[W_BOARD].handle;
1283     menuBarWidget    = optList[W_MENU].handle;
1284     dropMenu         = optList[W_DROP].handle;
1285     titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1286     formWidget  = XtParent(boardWidget);
1287     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1288     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1289     XtGetValues(optList[W_WHITE].handle, args, 2);
1290     if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1291       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1292       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1293       XtGetValues(optList[W_PAUSE].handle, args, 2);
1294     }
1295     AppendEnginesToMenu(appData.recentEngineList);
1296
1297     xBoardWindow = XtWindow(boardWidget);
1298
1299     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1300     //       not need to go into InitDrawingSizes().
1301
1302     /*
1303      * Create X checkmark bitmap and initialize option menu checks.
1304      */
1305     ReadBitmap(&xMarkPixmap, "checkmark.bm",
1306                checkmark_bits, checkmark_width, checkmark_height);
1307     InitMenuMarkers();
1308
1309     /*
1310      * Create an icon.
1311      */
1312     ReadBitmap(&wIconPixmap, "icon_white.bm",
1313                icon_white_bits, icon_white_width, icon_white_height);
1314     ReadBitmap(&bIconPixmap, "icon_black.bm",
1315                icon_black_bits, icon_black_width, icon_black_height);
1316     iconPixmap = wIconPixmap;
1317     i = 0;
1318     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
1319     XtSetValues(shellWidget, args, i);
1320
1321     /*
1322      * Create a cursor for the board widget.
1323      */
1324     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1325     XChangeWindowAttributes(xDisplay, xBoardWindow,
1326                             CWCursor, &window_attributes);
1327
1328     /*
1329      * Inhibit shell resizing.
1330      */
1331     shellArgs[0].value = (XtArgVal) &w;
1332     shellArgs[1].value = (XtArgVal) &h;
1333     XtGetValues(shellWidget, shellArgs, 2);
1334     shellArgs[4].value = shellArgs[2].value = w;
1335     shellArgs[5].value = shellArgs[3].value = h;
1336     if(!CreateAnyPieces()) XtSetValues(shellWidget, &shellArgs[2], 4);
1337     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1338     marginH =  h - boardHeight;
1339
1340     CatchDeleteWindow(shellWidget, "QuitProc");
1341
1342     CreateGCs(False);
1343     CreateGrid();
1344
1345     if(appData.logoSize)
1346     {   // locate and read user logo
1347         char buf[MSG_SIZ];
1348         snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1349         ASSIGN(userLogo, buf);
1350     }
1351
1352     if (appData.animate || appData.animateDragging)
1353       CreateAnimVars();
1354
1355     XtAugmentTranslations(formWidget,
1356                           XtParseTranslationTable(globalTranslations));
1357
1358     XtAddEventHandler(formWidget, KeyPressMask, False,
1359                       (XtEventHandler) MoveTypeInProc, NULL);
1360     XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1361                       (XtEventHandler) EventProc, NULL);
1362
1363     /* [AS] Restore layout */
1364     if( wpMoveHistory.visible ) {
1365       HistoryPopUp();
1366     }
1367
1368     if( wpEvalGraph.visible )
1369       {
1370         EvalGraphPopUp();
1371       };
1372
1373     if( wpEngineOutput.visible ) {
1374       EngineOutputPopUp();
1375     }
1376
1377     InitBackEnd2();
1378
1379     if (errorExitStatus == -1) {
1380         if (appData.icsActive) {
1381             /* We now wait until we see "login:" from the ICS before
1382                sending the logon script (problems with timestamp otherwise) */
1383             /*ICSInitScript();*/
1384             if (appData.icsInputBox) ICSInputBoxPopUp();
1385         }
1386
1387     #ifdef SIGWINCH
1388     signal(SIGWINCH, TermSizeSigHandler);
1389     #endif
1390         signal(SIGINT, IntSigHandler);
1391         signal(SIGTERM, IntSigHandler);
1392         if (*appData.cmailGameName != NULLCHAR) {
1393             signal(SIGUSR1, CmailSigHandler);
1394         }
1395     }
1396
1397     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1398     InitPosition(TRUE);
1399     UpdateLogos(TRUE);
1400 //    XtSetKeyboardFocus(shellWidget, formWidget);
1401     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1402
1403     XtAppMainLoop(appContext);
1404     if (appData.debugMode) fclose(debugFP); // [DM] debug
1405     return 0;
1406 }
1407
1408 RETSIGTYPE
1409 TermSizeSigHandler (int sig)
1410 {
1411     update_ics_width();
1412 }
1413
1414 RETSIGTYPE
1415 IntSigHandler (int sig)
1416 {
1417     ExitEvent(sig);
1418 }
1419
1420 RETSIGTYPE
1421 CmailSigHandler (int sig)
1422 {
1423     int dummy = 0;
1424     int error;
1425
1426     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
1427
1428     /* Activate call-back function CmailSigHandlerCallBack()             */
1429     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1430
1431     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1432 }
1433
1434 void
1435 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1436 {
1437     BoardToTop();
1438     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
1439 }
1440 /**** end signal code ****/
1441
1442
1443 #define Abs(n) ((n)<0 ? -(n) : (n))
1444
1445 #ifdef ENABLE_NLS
1446 char *
1447 InsertPxlSize (char *pattern, int targetPxlSize)
1448 {
1449     char *base_fnt_lst, strInt[12], *p, *q;
1450     int alternatives, i, len, strIntLen;
1451
1452     /*
1453      * Replace the "*" (if present) in the pixel-size slot of each
1454      * alternative with the targetPxlSize.
1455      */
1456     p = pattern;
1457     alternatives = 1;
1458     while ((p = strchr(p, ',')) != NULL) {
1459       alternatives++;
1460       p++;
1461     }
1462     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1463     strIntLen = strlen(strInt);
1464     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1465
1466     p = pattern;
1467     q = base_fnt_lst;
1468     while (alternatives--) {
1469       char *comma = strchr(p, ',');
1470       for (i=0; i<14; i++) {
1471         char *hyphen = strchr(p, '-');
1472         if (!hyphen) break;
1473         if (comma && hyphen > comma) break;
1474         len = hyphen + 1 - p;
1475         if (i == 7 && *p == '*' && len == 2) {
1476           p += len;
1477           memcpy(q, strInt, strIntLen);
1478           q += strIntLen;
1479           *q++ = '-';
1480         } else {
1481           memcpy(q, p, len);
1482           p += len;
1483           q += len;
1484         }
1485       }
1486       if (!comma) break;
1487       len = comma + 1 - p;
1488       memcpy(q, p, len);
1489       p += len;
1490       q += len;
1491     }
1492     strcpy(q, p);
1493
1494     return base_fnt_lst;
1495 }
1496
1497 XFontSet
1498 CreateFontSet (char *base_fnt_lst)
1499 {
1500     XFontSet fntSet;
1501     char **missing_list;
1502     int missing_count;
1503     char *def_string;
1504
1505     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1506                             &missing_list, &missing_count, &def_string);
1507     if (appData.debugMode) {
1508       int i, count;
1509       XFontStruct **font_struct_list;
1510       char **font_name_list;
1511       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1512       if (fntSet) {
1513         fprintf(debugFP, " got list %s, locale %s\n",
1514                 XBaseFontNameListOfFontSet(fntSet),
1515                 XLocaleOfFontSet(fntSet));
1516         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1517         for (i = 0; i < count; i++) {
1518           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1519         }
1520       }
1521       for (i = 0; i < missing_count; i++) {
1522         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1523       }
1524     }
1525     if (fntSet == NULL) {
1526       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1527       exit(2);
1528     }
1529     return fntSet;
1530 }
1531 #else // not ENABLE_NLS
1532 /*
1533  * Find a font that matches "pattern" that is as close as
1534  * possible to the targetPxlSize.  Prefer fonts that are k
1535  * pixels smaller to fonts that are k pixels larger.  The
1536  * pattern must be in the X Consortium standard format,
1537  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1538  * The return value should be freed with XtFree when no
1539  * longer needed.
1540  */
1541 char *
1542 FindFont (char *pattern, int targetPxlSize)
1543 {
1544     char **fonts, *p, *best, *scalable, *scalableTail;
1545     int i, j, nfonts, minerr, err, pxlSize;
1546
1547     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1548     if (nfonts < 1) {
1549         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1550                 programName, pattern);
1551         exit(2);
1552     }
1553
1554     best = fonts[0];
1555     scalable = NULL;
1556     minerr = 999999;
1557     for (i=0; i<nfonts; i++) {
1558         j = 0;
1559         p = fonts[i];
1560         if (*p != '-') continue;
1561         while (j < 7) {
1562             if (*p == NULLCHAR) break;
1563             if (*p++ == '-') j++;
1564         }
1565         if (j < 7) continue;
1566         pxlSize = atoi(p);
1567         if (pxlSize == 0) {
1568             scalable = fonts[i];
1569             scalableTail = p;
1570         } else {
1571             err = pxlSize - targetPxlSize;
1572             if (Abs(err) < Abs(minerr) ||
1573                 (minerr > 0 && err < 0 && -err == minerr)) {
1574                 best = fonts[i];
1575                 minerr = err;
1576             }
1577         }
1578     }
1579     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1580         /* If the error is too big and there is a scalable font,
1581            use the scalable font. */
1582         int headlen = scalableTail - scalable;
1583         p = (char *) XtMalloc(strlen(scalable) + 10);
1584         while (isdigit(*scalableTail)) scalableTail++;
1585         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1586     } else {
1587         p = (char *) XtMalloc(strlen(best) + 2);
1588         safeStrCpy(p, best, strlen(best)+1 );
1589     }
1590     if (appData.debugMode) {
1591         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
1592                 pattern, targetPxlSize, p);
1593     }
1594     XFreeFontNames(fonts);
1595     return p;
1596 }
1597 #endif
1598
1599 void
1600 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1601 {
1602     int x_hot, y_hot;
1603     u_int w, h;
1604     int errcode;
1605     char msg[MSG_SIZ], fullname[MSG_SIZ];
1606
1607     if (*appData.bitmapDirectory != NULLCHAR) {
1608       safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
1609       strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
1610       strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
1611       errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
1612                                 &w, &h, pm, &x_hot, &y_hot);
1613       fprintf(stderr, "load %s\n", name);
1614         if (errcode != BitmapSuccess) {
1615             switch (errcode) {
1616               case BitmapOpenFailed:
1617                 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
1618                 break;
1619               case BitmapFileInvalid:
1620                 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
1621                 break;
1622               case BitmapNoMemory:
1623                 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
1624                         fullname);
1625                 break;
1626               default:
1627                 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
1628                         errcode, fullname);
1629                 break;
1630             }
1631             fprintf(stderr, _("%s: %s...using built-in\n"),
1632                     programName, msg);
1633         } else if (w != wreq || h != hreq) {
1634             fprintf(stderr,
1635                     _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
1636                     programName, fullname, w, h, wreq, hreq);
1637         } else {
1638             return;
1639         }
1640     }
1641     if (bits != NULL) {
1642         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1643                                     wreq, hreq);
1644     }
1645 }
1646
1647 void
1648 MarkMenuItem (char *menuRef, int state)
1649 {
1650     MenuItem *item = MenuNameToItem(menuRef);
1651
1652     if(item) {
1653         Arg args[2];
1654         XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1655         XtSetValues(item->handle, args, 1);
1656     }
1657 }
1658
1659 void
1660 EnableNamedMenuItem (char *menuRef, int state)
1661 {
1662     MenuItem *item = MenuNameToItem(menuRef);
1663
1664     if(item) XtSetSensitive(item->handle, state);
1665 }
1666
1667 void
1668 EnableButtonBar (int state)
1669 {
1670     XtSetSensitive(optList[W_BUTTON].handle, state);
1671 }
1672
1673
1674 void
1675 SetMenuEnables (Enables *enab)
1676 {
1677   while (enab->name != NULL) {
1678     EnableNamedMenuItem(enab->name, enab->value);
1679     enab++;
1680   }
1681 }
1682
1683 void
1684 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1685 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1686     MenuItem *item;
1687     if(*nprms == 0) return;
1688     item = MenuNameToItem(prms[0]);
1689     if(item) ((MenuProc *) item->proc) ();
1690 }
1691
1692 static void
1693 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
1694 {
1695     RecentEngineEvent((int) (intptr_t) addr);
1696 }
1697
1698 void
1699 AppendMenuItem (char *msg, int n)
1700 {
1701     CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
1702 }
1703
1704 void
1705 SetupDropMenu ()
1706 {
1707     int i, j, count;
1708     char label[32];
1709     Arg args[16];
1710     Widget entry;
1711     char* p;
1712
1713     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1714         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1715         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1716                    dmEnables[i].piece);
1717         XtSetSensitive(entry, p != NULL || !appData.testLegality
1718                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1719                                        && !appData.icsActive));
1720         count = 0;
1721         while (p && *p++ == dmEnables[i].piece) count++;
1722         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
1723         j = 0;
1724         XtSetArg(args[j], XtNlabel, label); j++;
1725         XtSetValues(entry, args, j);
1726     }
1727 }
1728
1729
1730 static void
1731 do_flash_delay (unsigned long msec)
1732 {
1733     TimeDelay(msec);
1734 }
1735
1736 void
1737 FlashDelay (int flash_delay)
1738 {
1739         XSync(xDisplay, False);
1740         if(flash_delay) do_flash_delay(flash_delay);
1741 }
1742
1743 double
1744 Fraction (int x, int start, int stop)
1745 {
1746    double f = ((double) x - start)/(stop - start);
1747    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1748    return f;
1749 }
1750
1751 static WindowPlacement wpNew;
1752
1753 void
1754 CoDrag (Widget sh, WindowPlacement *wp)
1755 {
1756     Arg args[16];
1757     int j=0, touch=0, fudge = 2;
1758     GetActualPlacement(sh, wp);
1759     if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x)         < fudge) touch = 1; else // right touch
1760     if(abs(wp->x + wp->width + 2*frameX - wpMain.x)            < fudge) touch = 2; else // left touch
1761     if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1762     if(abs(wp->y + wp->height + frameX + frameY - wpMain.y)    < fudge) touch = 4;      // top touch
1763     if(!touch ) return; // only windows that touch co-move
1764     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1765         int heightInc = wpNew.height - wpMain.height;
1766         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1767         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1768         wp->y += fracTop * heightInc;
1769         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1770         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1771     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1772         int widthInc = wpNew.width - wpMain.width;
1773         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1774         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1775         wp->y += fracLeft * widthInc;
1776         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1777         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1778     }
1779     wp->x += wpNew.x - wpMain.x;
1780     wp->y += wpNew.y - wpMain.y;
1781     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1782     if(touch == 3) wp->y += wpNew.height - wpMain.height;
1783     XtSetArg(args[j], XtNx, wp->x); j++;
1784     XtSetArg(args[j], XtNy, wp->y); j++;
1785     XtSetValues(sh, args, j);
1786 }
1787
1788 void
1789 ReSize (WindowPlacement *wp)
1790 {
1791         int sqx, sqy;
1792         if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1793         sqx = (wp->width  - lineGap - marginW) / BOARD_WIDTH - lineGap;
1794         sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1795         if(sqy < sqx) sqx = sqy;
1796         if(sqx != squareSize) {
1797             squareSize = sqx; // adopt new square size
1798             NewSurfaces();
1799             CreatePNGPieces(); // make newly scaled pieces
1800             InitDrawingSizes(0, 0); // creates grid etc.
1801         }
1802 }
1803
1804 static XtIntervalId delayedDragID = 0;
1805
1806 void
1807 DragProc ()
1808 {
1809         static int busy;
1810         if(busy) return;
1811
1812         busy = 1;
1813         GetActualPlacement(shellWidget, &wpNew);
1814         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1815            wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1816             busy = 0; return; // false alarm
1817         }
1818         ReSize(&wpNew);
1819         if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1820         if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1821         if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1822         if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1823         wpMain = wpNew;
1824         DrawPosition(True, NULL);
1825         delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1826         busy = 0;
1827 }
1828
1829
1830 void
1831 DelayedDrag ()
1832 {
1833     if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1834     delayedDragID =
1835       XtAppAddTimeOut(appContext, 100, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1836 }
1837
1838 void
1839 EventProc (Widget widget, caddr_t unused, XEvent *event)
1840 {
1841     if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1842         DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1843 }
1844
1845 /*
1846  * event handler for redrawing the board
1847  */
1848 void
1849 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1850 {
1851     DrawPosition(True, NULL);
1852 }
1853
1854
1855 void
1856 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1857 {   // [HGM] pv: walk PV
1858     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1859 }
1860
1861 static int savedIndex;  /* gross that this is global */
1862
1863 void
1864 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1865 {
1866         String val;
1867         XawTextPosition index, dummy;
1868         Arg arg;
1869
1870         XawTextGetSelectionPos(w, &index, &dummy);
1871         XtSetArg(arg, XtNstring, &val);
1872         XtGetValues(w, &arg, 1);
1873         ReplaceComment(savedIndex, val);
1874         if(savedIndex != currentMove) ToNrEvent(savedIndex);
1875         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1876 }
1877
1878 void
1879 EditCommentPopUp (int index, char *title, char *text)
1880 {
1881     savedIndex = index;
1882     if (text == NULL) text = "";
1883     NewCommentPopup(title, text, index);
1884 }
1885
1886 void
1887 CommentPopUp (char *title, char *text)
1888 {
1889     savedIndex = currentMove; // [HGM] vari
1890     NewCommentPopup(title, text, currentMove);
1891 }
1892
1893 void
1894 CommentPopDown ()
1895 {
1896     PopDown(CommentDlg);
1897 }
1898
1899
1900 /* Disable all user input other than deleting the window */
1901 static int frozen = 0;
1902
1903 void
1904 FreezeUI ()
1905 {
1906   if (frozen) return;
1907   /* Grab by a widget that doesn't accept input */
1908   XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1909   frozen = 1;
1910 }
1911
1912 /* Undo a FreezeUI */
1913 void
1914 ThawUI ()
1915 {
1916   if (!frozen) return;
1917   XtRemoveGrab(optList[W_MESSG].handle);
1918   frozen = 0;
1919 }
1920
1921 void
1922 ModeHighlight ()
1923 {
1924     Arg args[16];
1925     static int oldPausing = FALSE;
1926     static GameMode oldmode = (GameMode) -1;
1927     char *wname;
1928
1929     if (!boardWidget || !XtIsRealized(boardWidget)) return;
1930
1931     if (pausing != oldPausing) {
1932         oldPausing = pausing;
1933         MarkMenuItem("Mode.Pause", pausing);
1934
1935         if (appData.showButtonBar) {
1936           /* Always toggle, don't set.  Previous code messes up when
1937              invoked while the button is pressed, as releasing it
1938              toggles the state again. */
1939           {
1940             Pixel oldbg, oldfg;
1941             XtSetArg(args[0], XtNbackground, &oldbg);
1942             XtSetArg(args[1], XtNforeground, &oldfg);
1943             XtGetValues(optList[W_PAUSE].handle,
1944                         args, 2);
1945             XtSetArg(args[0], XtNbackground, oldfg);
1946             XtSetArg(args[1], XtNforeground, oldbg);
1947           }
1948           XtSetValues(optList[W_PAUSE].handle, args, 2);
1949         }
1950     }
1951
1952     wname = ModeToWidgetName(oldmode);
1953     if (wname != NULL) {
1954         MarkMenuItem(wname, False);
1955     }
1956     wname = ModeToWidgetName(gameMode);
1957     if (wname != NULL) {
1958         MarkMenuItem(wname, True);
1959     }
1960     oldmode = gameMode;
1961     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1962
1963     /* Maybe all the enables should be handled here, not just this one */
1964     EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1965
1966     DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
1967 }
1968
1969
1970 /*
1971  * Button/menu procedures
1972  */
1973
1974 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1975 char *selected_fen_position=NULL;
1976
1977 Boolean
1978 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1979                        Atom *type_return, XtPointer *value_return,
1980                        unsigned long *length_return, int *format_return)
1981 {
1982   char *selection_tmp;
1983
1984 //  if (!selected_fen_position) return False; /* should never happen */
1985   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1986    if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1987     FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1988     long len;
1989     size_t count;
1990     if (f == NULL) return False;
1991     fseek(f, 0, 2);
1992     len = ftell(f);
1993     rewind(f);
1994     selection_tmp = XtMalloc(len + 1);
1995     count = fread(selection_tmp, 1, len, f);
1996     fclose(f);
1997     if (len != count) {
1998       XtFree(selection_tmp);
1999       return False;
2000     }
2001     selection_tmp[len] = NULLCHAR;
2002    } else {
2003     /* note: since no XtSelectionDoneProc was registered, Xt will
2004      * automatically call XtFree on the value returned.  So have to
2005      * make a copy of it allocated with XtMalloc */
2006     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
2007     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
2008    }
2009
2010     *value_return=selection_tmp;
2011     *length_return=strlen(selection_tmp);
2012     *type_return=*target;
2013     *format_return = 8; /* bits per byte */
2014     return True;
2015   } else if (*target == XA_TARGETS(xDisplay)) {
2016     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
2017     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
2018     targets_tmp[1] = XA_STRING;
2019     *value_return = targets_tmp;
2020     *type_return = XA_ATOM;
2021     *length_return = 2;
2022 #if 0
2023     // This code leads to a read of value_return out of bounds on 64-bit systems.
2024     // Other code which I have seen always sets *format_return to 32 independent of
2025     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
2026     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
2027     *format_return = 8 * sizeof(Atom);
2028     if (*format_return > 32) {
2029       *length_return *= *format_return / 32;
2030       *format_return = 32;
2031     }
2032 #else
2033     *format_return = 32;
2034 #endif
2035     return True;
2036   } else {
2037     return False;
2038   }
2039 }
2040
2041 /* note: when called from menu all parameters are NULL, so no clue what the
2042  * Widget which was clicked on was, or what the click event was
2043  */
2044 void
2045 CopySomething (char *src)
2046 {
2047     selected_fen_position = src;
2048     /*
2049      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2050      * have a notion of a position that is selected but not copied.
2051      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2052      */
2053     XtOwnSelection(menuBarWidget, XA_PRIMARY,
2054                    CurrentTime,
2055                    SendPositionSelection,
2056                    NULL/* lose_ownership_proc */ ,
2057                    NULL/* transfer_done_proc */);
2058     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2059                    CurrentTime,
2060                    SendPositionSelection,
2061                    NULL/* lose_ownership_proc */ ,
2062                    NULL/* transfer_done_proc */);
2063 }
2064
2065 /* function called when the data to Paste is ready */
2066 static void
2067 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2068                  Atom *type, XtPointer value, unsigned long *len, int *format)
2069 {
2070   char *fenstr=value;
2071   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2072   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2073   EditPositionPasteFEN(fenstr);
2074   XtFree(value);
2075 }
2076
2077 /* called when Paste Position button is pressed,
2078  * all parameters will be NULL */
2079 void
2080 PastePositionProc ()
2081 {
2082     XtGetSelectionValue(menuBarWidget,
2083       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2084       /* (XtSelectionCallbackProc) */ PastePositionCB,
2085       NULL, /* client_data passed to PastePositionCB */
2086
2087       /* better to use the time field from the event that triggered the
2088        * call to this function, but that isn't trivial to get
2089        */
2090       CurrentTime
2091     );
2092     return;
2093 }
2094
2095 /* note: when called from menu all parameters are NULL, so no clue what the
2096  * Widget which was clicked on was, or what the click event was
2097  */
2098 /* function called when the data to Paste is ready */
2099 static void
2100 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2101              Atom *type, XtPointer value, unsigned long *len, int *format)
2102 {
2103   FILE* f;
2104   if (value == NULL || *len == 0) {
2105     return; /* nothing had been selected to copy */
2106   }
2107   f = fopen(gamePasteFilename, "w");
2108   if (f == NULL) {
2109     DisplayError(_("Can't open temp file"), errno);
2110     return;
2111   }
2112   fwrite(value, 1, *len, f);
2113   fclose(f);
2114   XtFree(value);
2115   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2116 }
2117
2118 /* called when Paste Game button is pressed,
2119  * all parameters will be NULL */
2120 void
2121 PasteGameProc ()
2122 {
2123     XtGetSelectionValue(menuBarWidget,
2124       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2125       /* (XtSelectionCallbackProc) */ PasteGameCB,
2126       NULL, /* client_data passed to PasteGameCB */
2127
2128       /* better to use the time field from the event that triggered the
2129        * call to this function, but that isn't trivial to get
2130        */
2131       CurrentTime
2132     );
2133     return;
2134 }
2135
2136
2137 void
2138 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2139 {
2140     QuitProc();
2141 }
2142
2143 int
2144 ShiftKeys ()
2145 {   // bassic primitive for determining if modifier keys are pressed
2146     long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2147     char keys[32];
2148     int i,j,  k=0;
2149     XQueryKeymap(xDisplay,keys);
2150     for(i=0; i<6; i++) {
2151         k <<= 1;
2152         j = XKeysymToKeycode(xDisplay, codes[i]);
2153         k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2154     }
2155     return k;
2156 }
2157
2158 static void
2159 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2160 {
2161     char buf[10];
2162     KeySym sym;
2163     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2164     if ( n == 1 && *buf >= 32 // printable
2165          && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2166         ) BoxAutoPopUp (buf);
2167 }
2168
2169 static void
2170 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2171 {   // [HGM] input: let up-arrow recall previous line from history
2172     IcsKey(1);
2173 }
2174
2175 static void
2176 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2177 {   // [HGM] input: let down-arrow recall next line from history
2178     IcsKey(-1);
2179 }
2180
2181 static void
2182 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2183 {
2184     IcsKey(0);
2185 }
2186
2187 void
2188 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2189 {
2190         if (!TempBackwardActive) {
2191                 TempBackwardActive = True;
2192                 BackwardEvent();
2193         }
2194 }
2195
2196 void
2197 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2198 {
2199         /* Check to see if triggered by a key release event for a repeating key.
2200          * If so the next queued event will be a key press of the same key at the same time */
2201         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2202                 XEvent next;
2203                 XPeekEvent(xDisplay, &next);
2204                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2205                         next.xkey.keycode == event->xkey.keycode)
2206                                 return;
2207         }
2208     ForwardEvent();
2209         TempBackwardActive = False;
2210 }
2211
2212 void
2213 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2214 {   // called as key binding
2215     char buf[MSG_SIZ];
2216     String name;
2217     if (nprms && *nprms > 0)
2218       name = prms[0];
2219     else
2220       name = "xboard";
2221     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2222     system(buf);
2223 }
2224
2225 void
2226 ManProc ()
2227 {   // called from menu
2228     ManInner(NULL, NULL, NULL, NULL);
2229 }
2230
2231 void
2232 SetWindowTitle (char *text, char *title, char *icon)
2233 {
2234     Arg args[16];
2235     int i;
2236     if (appData.titleInWindow) {
2237         i = 0;
2238         XtSetArg(args[i], XtNlabel, text);   i++;
2239         XtSetValues(titleWidget, args, i);
2240     }
2241     i = 0;
2242     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
2243     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
2244     XtSetValues(shellWidget, args, i);
2245     XSync(xDisplay, False);
2246 }
2247
2248
2249 static int
2250 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2251 {
2252     return 0;
2253 }
2254
2255 void
2256 DisplayIcsInteractionTitle (String message)
2257 {
2258   if (oldICSInteractionTitle == NULL) {
2259     /* Magic to find the old window title, adapted from vim */
2260     char *wina = getenv("WINDOWID");
2261     if (wina != NULL) {
2262       Window win = (Window) atoi(wina);
2263       Window root, parent, *children;
2264       unsigned int nchildren;
2265       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2266       for (;;) {
2267         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2268         if (!XQueryTree(xDisplay, win, &root, &parent,
2269                         &children, &nchildren)) break;
2270         if (children) XFree((void *)children);
2271         if (parent == root || parent == 0) break;
2272         win = parent;
2273       }
2274       XSetErrorHandler(oldHandler);
2275     }
2276     if (oldICSInteractionTitle == NULL) {
2277       oldICSInteractionTitle = "xterm";
2278     }
2279   }
2280   printf("\033]0;%s\007", message);
2281   fflush(stdout);
2282 }
2283
2284
2285 XtIntervalId delayedEventTimerXID = 0;
2286 DelayedEventCallback delayedEventCallback = 0;
2287
2288 void
2289 FireDelayedEvent ()
2290 {
2291     delayedEventTimerXID = 0;
2292     delayedEventCallback();
2293 }
2294
2295 void
2296 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2297 {
2298     if(delayedEventTimerXID && delayedEventCallback == cb)
2299         // [HGM] alive: replace, rather than add or flush identical event
2300         XtRemoveTimeOut(delayedEventTimerXID);
2301     delayedEventCallback = cb;
2302     delayedEventTimerXID =
2303       XtAppAddTimeOut(appContext, millisec,
2304                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2305 }
2306
2307 DelayedEventCallback
2308 GetDelayedEvent ()
2309 {
2310   if (delayedEventTimerXID) {
2311     return delayedEventCallback;
2312   } else {
2313     return NULL;
2314   }
2315 }
2316
2317 void
2318 CancelDelayedEvent ()
2319 {
2320   if (delayedEventTimerXID) {
2321     XtRemoveTimeOut(delayedEventTimerXID);
2322     delayedEventTimerXID = 0;
2323   }
2324 }
2325
2326 XtIntervalId loadGameTimerXID = 0;
2327
2328 int
2329 LoadGameTimerRunning ()
2330 {
2331     return loadGameTimerXID != 0;
2332 }
2333
2334 int
2335 StopLoadGameTimer ()
2336 {
2337     if (loadGameTimerXID != 0) {
2338         XtRemoveTimeOut(loadGameTimerXID);
2339         loadGameTimerXID = 0;
2340         return TRUE;
2341     } else {
2342         return FALSE;
2343     }
2344 }
2345
2346 void
2347 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2348 {
2349     loadGameTimerXID = 0;
2350     AutoPlayGameLoop();
2351 }
2352
2353 void
2354 StartLoadGameTimer (long millisec)
2355 {
2356     loadGameTimerXID =
2357       XtAppAddTimeOut(appContext, millisec,
2358                       (XtTimerCallbackProc) LoadGameTimerCallback,
2359                       (XtPointer) 0);
2360 }
2361
2362 XtIntervalId analysisClockXID = 0;
2363
2364 void
2365 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2366 {
2367     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2368          || appData.icsEngineAnalyze) { // [DM]
2369         AnalysisPeriodicEvent(0);
2370         StartAnalysisClock();
2371     }
2372 }
2373
2374 void
2375 StartAnalysisClock ()
2376 {
2377     analysisClockXID =
2378       XtAppAddTimeOut(appContext, 2000,
2379                       (XtTimerCallbackProc) AnalysisClockCallback,
2380                       (XtPointer) 0);
2381 }
2382
2383 XtIntervalId clockTimerXID = 0;
2384
2385 int
2386 ClockTimerRunning ()
2387 {
2388     return clockTimerXID != 0;
2389 }
2390
2391 int
2392 StopClockTimer ()
2393 {
2394     if (clockTimerXID != 0) {
2395         XtRemoveTimeOut(clockTimerXID);
2396         clockTimerXID = 0;
2397         return TRUE;
2398     } else {
2399         return FALSE;
2400     }
2401 }
2402
2403 void
2404 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2405 {
2406     clockTimerXID = 0;
2407     DecrementClocks();
2408 }
2409
2410 void
2411 StartClockTimer (long millisec)
2412 {
2413     clockTimerXID =
2414       XtAppAddTimeOut(appContext, millisec,
2415                       (XtTimerCallbackProc) ClockTimerCallback,
2416                       (XtPointer) 0);
2417 }
2418
2419 void
2420 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2421 {
2422     char buf[MSG_SIZ];
2423     Arg args[16];
2424     Widget w = (Widget) opt->handle;
2425
2426     /* check for low time warning */
2427     Pixel foregroundOrWarningColor = timerForegroundPixel;
2428
2429     if (timer > 0 &&
2430         appData.lowTimeWarning &&
2431         (timer / 1000) < appData.icsAlarmTime)
2432       foregroundOrWarningColor = lowTimeWarningColor;
2433
2434     if (appData.clockMode) {
2435       snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2436       XtSetArg(args[0], XtNlabel, buf);
2437     } else {
2438       snprintf(buf, MSG_SIZ, "%s  ", color);
2439       XtSetArg(args[0], XtNlabel, buf);
2440     }
2441
2442     if (highlight) {
2443
2444         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2445         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2446     } else {
2447         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2448         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2449     }
2450
2451     XtSetValues(w, args, 3);
2452 }
2453
2454 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2455
2456 void
2457 SetClockIcon (int color)
2458 {
2459     Arg args[16];
2460     Pixmap pm = *clockIcons[color];
2461     if (iconPixmap != pm) {
2462         iconPixmap = pm;
2463         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2464         XtSetValues(shellWidget, args, 1);
2465     }
2466 }
2467
2468 void
2469 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2470 {
2471     InputSource *is = (InputSource *) closure;
2472     int count;
2473     int error;
2474     char *p, *q;
2475
2476     if (is->lineByLine) {
2477         count = read(is->fd, is->unused,
2478                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2479         if (count <= 0) {
2480             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2481             return;
2482         }
2483         is->unused += count;
2484         p = is->buf;
2485         while (p < is->unused) {
2486             q = memchr(p, '\n', is->unused - p);
2487             if (q == NULL) break;
2488             q++;
2489             (is->func)(is, is->closure, p, q - p, 0);
2490             p = q;
2491         }
2492         q = is->buf;
2493         while (p < is->unused) {
2494             *q++ = *p++;
2495         }
2496         is->unused = q;
2497     } else {
2498         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2499         if (count == -1)
2500           error = errno;
2501         else
2502           error = 0;
2503         (is->func)(is, is->closure, is->buf, count, error);
2504     }
2505 }
2506
2507 InputSourceRef
2508 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2509 {
2510     InputSource *is;
2511     ChildProc *cp = (ChildProc *) pr;
2512
2513     is = (InputSource *) calloc(1, sizeof(InputSource));
2514     is->lineByLine = lineByLine;
2515     is->func = func;
2516     if (pr == NoProc) {
2517         is->kind = CPReal;
2518         is->fd = fileno(stdin);
2519     } else {
2520         is->kind = cp->kind;
2521         is->fd = cp->fdFrom;
2522     }
2523     if (lineByLine) {
2524         is->unused = is->buf;
2525     }
2526
2527     is->xid = XtAppAddInput(appContext, is->fd,
2528                             (XtPointer) (XtInputReadMask),
2529                             (XtInputCallbackProc) DoInputCallback,
2530                             (XtPointer) is);
2531     is->closure = closure;
2532     return (InputSourceRef) is;
2533 }
2534
2535 void
2536 RemoveInputSource (InputSourceRef isr)
2537 {
2538     InputSource *is = (InputSource *) isr;
2539
2540     if (is->xid == 0) return;
2541     XtRemoveInput(is->xid);
2542     is->xid = 0;
2543 }
2544
2545 #ifndef HAVE_USLEEP
2546
2547 static Boolean frameWaiting;
2548
2549 static RETSIGTYPE
2550 FrameAlarm (int sig)
2551 {
2552   frameWaiting = False;
2553   /* In case System-V style signals.  Needed?? */
2554   signal(SIGALRM, FrameAlarm);
2555 }
2556
2557 void
2558 FrameDelay (int time)
2559 {
2560   struct itimerval delay;
2561
2562   XSync(xDisplay, False);
2563
2564   if (time > 0) {
2565     frameWaiting = True;
2566     signal(SIGALRM, FrameAlarm);
2567     delay.it_interval.tv_sec =
2568       delay.it_value.tv_sec = time / 1000;
2569     delay.it_interval.tv_usec =
2570       delay.it_value.tv_usec = (time % 1000) * 1000;
2571     setitimer(ITIMER_REAL, &delay, NULL);
2572     while (frameWaiting) pause();
2573     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2574     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2575     setitimer(ITIMER_REAL, &delay, NULL);
2576   }
2577 }
2578
2579 #else
2580
2581 void
2582 FrameDelay (int time)
2583 {
2584   XSync(xDisplay, False);
2585   if (time > 0)
2586     usleep(time * 1000);
2587 }
2588
2589 #endif
2590
2591 static void
2592 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2593 {
2594     char buf[MSG_SIZ], *logoName = buf;
2595     if(appData.logo[n][0]) {
2596         logoName = appData.logo[n];
2597     } else if(appData.autoLogo) {
2598         if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2599             sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2600         } else if(appData.directory[n] && appData.directory[n][0]) {
2601             sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2602         }
2603     }
2604     if(logoName[0])
2605         { ASSIGN(cps->programLogo, logoName); }
2606 }
2607
2608 void
2609 UpdateLogos (int displ)
2610 {
2611     if(optList[W_WHITE-1].handle == NULL) return;
2612     LoadLogo(&first, 0, 0);
2613     LoadLogo(&second, 1, appData.icsActive);
2614     if(displ) DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
2615     return;
2616 }
2617