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