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