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