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