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