Fix name of master settings file in OS X
[xboard.git] / gtk / 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 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 #include <gtk/gtk.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 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
153 #include "common.h"
154
155 #include "frontend.h"
156 #include "backend.h"
157 #include "backendz.h"
158 #include "moves.h"
159 #include "xboard.h"
160 #include "xboard2.h"
161 #include "childio.h"
162 #include "menus.h"
163 #include "board.h"
164 #include "dialogs.h"
165 #include "engineoutput.h"
166 #include "usystem.h"
167 #include "gettext.h"
168 #include "draw.h"
169
170 #ifdef __APPLE__
171 #  include <gtkmacintegration/gtkosxapplication.h>
172    // prevent pathname of positional file argument provided by OS X being be mistaken for option name
173    // (price is that we won't recognize Windows option format anymore).
174 #  define SLASH '-'
175    // redefine some defaults
176 #  undef ICS_LOGON
177 #  undef DATADIR
178 #  undef SETTINGS_FILE
179 #  define ICS_LOGON "Library/Preferences/XboardICS.conf"
180 #  define DATADIR dataDir
181 #  define SETTINGS_FILE masterSettings
182    char dataDir[MSG_SIZ]; // for expanding ~~
183    char masterSettings[MSG_SIZ];
184 #else
185 #  define SLASH '/'
186 #endif
187
188 #ifdef __EMX__
189 #ifndef HAVE_USLEEP
190 #define HAVE_USLEEP
191 #endif
192 #define usleep(t)   _sleep2(((t)+500)/1000)
193 #endif
194
195 #ifdef ENABLE_NLS
196 # define  _(s) gettext (s)
197 # define N_(s) gettext_noop (s)
198 #else
199 # define  _(s) (s)
200 # define N_(s)  s
201 #endif
202
203 int main P((int argc, char **argv));
204 RETSIGTYPE CmailSigHandler P((int sig));
205 RETSIGTYPE IntSigHandler P((int sig));
206 RETSIGTYPE TermSizeSigHandler P((int sig));
207 #if ENABLE_NLS
208 char *InsertPxlSize P((char *pattern, int targetPxlSize));
209 XFontSet CreateFontSet P((char *base_fnt_lst));
210 #else
211 char *FindFont P((char *pattern, int targetPxlSize));
212 #endif
213 void DelayedDrag P((void));
214 void ICSInputBoxPopUp P((void));
215 void MoveTypeInProc P((GdkEventKey *eventkey));
216 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
217 Boolean TempBackwardActive = False;
218 void DisplayMove P((int moveNumber));
219 void update_ics_width P(());
220 int CopyMemoProc P(());
221 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
222
223 #ifdef TODO_GTK
224 #if ENABLE_NLS
225 XFontSet fontSet, clockFontSet;
226 #else
227 Font clockFontID;
228 XFontStruct *clockFontStruct;
229 #endif
230 Font coordFontID, countFontID;
231 XFontStruct *coordFontStruct, *countFontStruct;
232 #else
233 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
234 GtkWidget       *mainwindow;
235 #endif
236 Option *optList; // contains all widgets of main window
237 char *layoutName;
238
239 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
240
241 /* pixbufs */
242 static GdkPixbuf       *mainwindowIcon=NULL;
243 static GdkPixbuf       *WhiteIcon=NULL;
244 static GdkPixbuf       *BlackIcon=NULL;
245
246 /* key board accelerators */
247 GtkAccelGroup *GtkAccelerators;
248
249 typedef unsigned int BoardSize;
250 BoardSize boardSize;
251 Boolean chessProgram;
252
253 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
254 int smallLayout = 0, tinyLayout = 0,
255   marginW, marginH, // [HGM] for run-time resizing
256   fromX = -1, fromY = -1, toX, toY, commentUp = False,
257   errorExitStatus = -1, defaultLineGap;
258 #ifdef TODO_GTK
259 Dimension textHeight;
260 #endif
261 char *chessDir, *programName, *programVersion;
262 Boolean alwaysOnTop = False;
263 char *icsTextMenuString;
264 char *icsNames;
265 char *firstChessProgramNames;
266 char *secondChessProgramNames;
267
268 WindowPlacement wpMain;
269 WindowPlacement wpConsole;
270 WindowPlacement wpComment;
271 WindowPlacement wpMoveHistory;
272 WindowPlacement wpEvalGraph;
273 WindowPlacement wpEngineOutput;
274 WindowPlacement wpGameList;
275 WindowPlacement wpTags;
276 WindowPlacement wpDualBoard;
277
278 /* This magic number is the number of intermediate frames used
279    in each half of the animation. For short moves it's reduced
280    by 1. The total number of frames will be factor * 2 + 1.  */
281 #define kFactor    4
282
283 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
284
285 typedef struct {
286     char piece;
287     char* widget;
288 } DropMenuEnables;
289
290 DropMenuEnables dmEnables[] = {
291     { 'P', "Pawn" },
292     { 'N', "Knight" },
293     { 'B', "Bishop" },
294     { 'R', "Rook" },
295     { 'Q', "Queen" }
296 };
297
298 #ifdef TODO_GTK
299 XtResource clientResources[] = {
300     { "flashCount", "flashCount", XtRInt, sizeof(int),
301         XtOffset(AppDataPtr, flashCount), XtRImmediate,
302         (XtPointer) FLASH_COUNT  },
303 };
304 #endif
305
306 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
307 char globalTranslations[] =
308   ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
309    :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
310    :<KeyDown>Return: TempBackwardProc() \n \
311    :<KeyUp>Return: TempForwardProc() \n";
312
313 char ICSInputTranslations[] =
314     "<Key>Up: UpKeyProc() \n "
315     "<Key>Down: DownKeyProc() \n "
316     "<Key>Return: EnterKeyProc() \n";
317
318 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
319 //             as the widget is destroyed before the up-click can call extend-end
320 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
321
322 #ifdef TODO_GTK
323 String xboardResources[] = {
324     "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
325     NULL
326   };
327 #endif
328
329 void
330 BoardToTop ()
331 {
332   gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
333 }
334
335 //---------------------------------------------------------------------------------------------------------
336 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
337 #define XBOARD True
338 #define JAWS_ARGS
339 #define CW_USEDEFAULT (1<<31)
340 #define ICS_TEXT_MENU_SIZE 90
341 #define DEBUG_FILE "xboard.debug"
342 #define SetCurrentDirectory chdir
343 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
344 #define OPTCHAR "-"
345 #define SEPCHAR " "
346
347 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
348 #include "args.h"
349
350 // front-end part of option handling
351
352 // [HGM] This platform-dependent table provides the location for storing the color info
353 extern char *crWhite, * crBlack;
354
355 void *
356 colorVariable[] = {
357   &appData.whitePieceColor,
358   &appData.blackPieceColor,
359   &appData.lightSquareColor,
360   &appData.darkSquareColor,
361   &appData.highlightSquareColor,
362   &appData.premoveHighlightColor,
363   &appData.lowTimeWarningColor,
364   NULL,
365   NULL,
366   NULL,
367   NULL,
368   NULL,
369   &crWhite,
370   &crBlack,
371   NULL
372 };
373
374 // [HGM] font: keep a font for each square size, even non-stndard ones
375 #define NUM_SIZES 18
376 #define MAX_SIZE 130
377 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
378 char *fontTable[NUM_FONTS][MAX_SIZE];
379
380 void
381 ParseFont (char *name, int number)
382 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
383   int size;
384   if(sscanf(name, "size%d:", &size)) {
385     // [HGM] font: font is meant for specific boardSize (likely from settings file);
386     //       defer processing it until we know if it matches our board size
387     if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
388         fontTable[number][size] = strdup(strchr(name, ':')+1);
389         fontValid[number][size] = True;
390     }
391     return;
392   }
393   switch(number) {
394     case 0: // CLOCK_FONT
395         appData.clockFont = strdup(name);
396       break;
397     case 1: // MESSAGE_FONT
398         appData.font = strdup(name);
399       break;
400     case 2: // COORD_FONT
401         appData.coordFont = strdup(name);
402       break;
403     default:
404       return;
405   }
406   fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
407 }
408
409 void
410 SetFontDefaults ()
411 { // only 2 fonts currently
412   appData.clockFont = CLOCK_FONT_NAME;
413   appData.coordFont = COORD_FONT_NAME;
414   appData.font  =   DEFAULT_FONT_NAME;
415 }
416
417 void
418 CreateFonts ()
419 { // no-op, until we identify the code for this already in XBoard and move it here
420 }
421
422 void
423 ParseColor (int n, char *name)
424 { // in XBoard, just copy the color-name string
425   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
426 }
427
428 void
429 ParseTextAttribs (ColorClass cc, char *s)
430 {
431     (&appData.colorShout)[cc] = strdup(s);
432 }
433
434 void
435 ParseBoardSize (void *addr, char *name)
436 {
437     appData.boardSize = strdup(name);
438 }
439
440 void
441 LoadAllSounds ()
442 { // In XBoard the sound-playing program takes care of obtaining the actual sound
443 }
444
445 void
446 SetCommPortDefaults ()
447 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
448 }
449
450 // [HGM] args: these three cases taken out to stay in front-end
451 void
452 SaveFontArg (FILE *f, ArgDescriptor *ad)
453 {
454   char *name;
455   int i, n = (int)(intptr_t)ad->argLoc;
456   switch(n) {
457     case 0: // CLOCK_FONT
458         name = appData.clockFont;
459       break;
460     case 1: // MESSAGE_FONT
461         name = appData.font;
462       break;
463     case 2: // COORD_FONT
464         name = appData.coordFont;
465       break;
466     default:
467       return;
468   }
469   for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
470     if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
471         fontTable[n][squareSize] = strdup(name);
472         fontValid[n][squareSize] = True;
473         break;
474   }
475   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
476     fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
477 }
478
479 void
480 ExportSounds ()
481 { // nothing to do, as the sounds are at all times represented by their text-string names already
482 }
483
484 void
485 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
486 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
487         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
488 }
489
490 void
491 SaveColor (FILE *f, ArgDescriptor *ad)
492 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
493         if(colorVariable[(int)(intptr_t)ad->argLoc])
494         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
495 }
496
497 void
498 SaveBoardSize (FILE *f, char *name, void *addr)
499 { // wrapper to shield back-end from BoardSize & sizeInfo
500   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
501 }
502
503 void
504 ParseCommPortSettings (char *s)
505 { // no such option in XBoard (yet)
506 }
507
508 int frameX, frameY;
509
510 void
511 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
512 {
513   GtkAllocation a;
514   if(!shell) return;
515   gtk_widget_get_allocation(shell, &a);
516   gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
517   wp->x = a.x;
518   wp->y = a.y;
519   wp->width = a.width;
520   wp->height = a.height;
521 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
522   frameX = 3; frameY = 3; // remember to decide if windows touch
523 }
524
525 void
526 GetWindowCoords ()
527 { // wrapper to shield use of window handles from back-end (make addressible by number?)
528   // In XBoard this will have to wait until awareness of window parameters is implemented
529   GetActualPlacement(shellWidget, &wpMain);
530   if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
531   if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
532   if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
533   if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
534   if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
535   if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
536 }
537
538 void
539 PrintCommPortSettings (FILE *f, char *name)
540 { // This option does not exist in XBoard
541 }
542
543 void
544 EnsureOnScreen (int *x, int *y, int minX, int minY)
545 {
546   return;
547 }
548
549 int
550 MainWindowUp ()
551 { // [HGM] args: allows testing if main window is realized from back-end
552   return DialogExists(BoardWindow);
553 }
554
555 void
556 PopUpStartupDialog ()
557 {  // start menu not implemented in XBoard
558 }
559
560 char *
561 ConvertToLine (int argc, char **argv)
562 {
563   static char line[128*1024], buf[1024];
564   int i;
565
566   line[0] = NULLCHAR;
567   for(i=1; i<argc; i++)
568     {
569       if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
570           && argv[i][0] != '{' )
571         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
572       else
573         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
574       strncat(line, buf, 128*1024 - strlen(line) - 1 );
575     }
576
577   line[strlen(line)-1] = NULLCHAR;
578   return line;
579 }
580
581 //--------------------------------------------------------------------------------------------
582
583 int clockKludge;
584
585 void
586 ResizeBoardWindow (int w, int h, int inhibit)
587 {
588     GtkAllocation a;
589     if(clockKludge) return; // ignore as long as clock does not have final height
590     gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
591     w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
592     h += marginH + a.height + 1;
593     gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
594 }
595
596 int
597 MakeColors ()
598 {   // dummy, as the GTK code does not make colors in advance
599     return FALSE;
600 }
601
602 void
603 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
604 {   // determine what fonts to use, and create them
605 #ifdef TODO_GTK
606     XrmValue vTo;
607     XrmDatabase xdb;
608
609     if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
610         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
611     if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
612         appData.font = fontTable[MESSAGE_FONT][squareSize];
613     if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
614         appData.coordFont = fontTable[COORD_FONT][squareSize];
615
616 #if ENABLE_NLS
617     appData.font = InsertPxlSize(appData.font, fontPxlSize);
618     appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
619     appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
620     fontSet = CreateFontSet(appData.font);
621     clockFontSet = CreateFontSet(appData.clockFont);
622     {
623       /* For the coordFont, use the 0th font of the fontset. */
624       XFontSet coordFontSet = CreateFontSet(appData.coordFont);
625       XFontStruct **font_struct_list;
626       XFontSetExtents *fontSize;
627       char **font_name_list;
628       XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
629       coordFontID = XLoadFont(xDisplay, font_name_list[0]);
630       coordFontStruct = XQueryFont(xDisplay, coordFontID);
631       fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
632       textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
633     }
634 #else
635     appData.font = FindFont(appData.font, fontPxlSize);
636     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
637     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
638     clockFontID = XLoadFont(xDisplay, appData.clockFont);
639     clockFontStruct = XQueryFont(xDisplay, clockFontID);
640     coordFontID = XLoadFont(xDisplay, appData.coordFont);
641     coordFontStruct = XQueryFont(xDisplay, coordFontID);
642     // textHeight in !NLS mode!
643 #endif
644     countFontID = coordFontID;  // [HGM] holdings
645     countFontStruct = coordFontStruct;
646
647     xdb = XtDatabase(xDisplay);
648 #if ENABLE_NLS
649     XrmPutLineResource(&xdb, "*international: True");
650     vTo.size = sizeof(XFontSet);
651     vTo.addr = (XtPointer) &fontSet;
652     XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
653 #else
654     XrmPutStringResource(&xdb, "*font", appData.font);
655 #endif
656 #endif
657 }
658
659 char *
660 PrintArg (ArgType t)
661 {
662   char *p="";
663   switch(t) {
664     case ArgZ:
665     case ArgInt:      p = " N"; break;
666     case ArgString:   p = " STR"; break;
667     case ArgBoolean:  p = " TF"; break;
668     case ArgSettingsFilename:
669     case ArgBackupSettingsFile:
670     case ArgFilename: p = " FILE"; break;
671     case ArgX:        p = " Nx"; break;
672     case ArgY:        p = " Ny"; break;
673     case ArgAttribs:  p = " TEXTCOL"; break;
674     case ArgColor:    p = " COL"; break;
675     case ArgFont:     p = " FONT"; break;
676     case ArgBoardSize: p = " SIZE"; break;
677     case ArgFloat: p = " FLOAT"; break;
678     case ArgTrue:
679     case ArgFalse:
680     case ArgTwo:
681     case ArgNone:
682     case ArgCommSettings:
683     default:
684       break;
685   }
686   return p;
687 }
688
689 void
690 PrintOptions ()
691 {
692   char buf[MSG_SIZ];
693   int len=0;
694   ArgDescriptor *q, *p = argDescriptors+5;
695   printf("\nXBoard accepts the following options:\n"
696          "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
697          " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
698          " SIZE = board-size spec(s)\n"
699          " Within parentheses are short forms, or options to set to true or false.\n"
700          " Persistent options (saved in the settings file) are marked with *)\n\n");
701   while(p->argName) {
702     if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
703     snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
704     if(p->save) strcat(buf+len, "*");
705     for(q=p+1; q->argLoc == p->argLoc; q++) {
706       if(q->argName[0] == '-') continue;
707       strcat(buf+len, q == p+1 ? " (" : " ");
708       sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
709     }
710     if(q != p+1) strcat(buf+len, ")");
711     len = strlen(buf);
712     if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
713     p = q;
714   }
715   if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
716 }
717
718 void
719 SlaveResize (Option *opt)
720 {
721     static int slaveW, slaveH, w, h;
722     GtkAllocation a;
723     if(!slaveH) {
724         gtk_widget_get_allocation(shells[DummyDlg], &a);
725         w = a.width; h = a.height;
726         gtk_widget_get_allocation(opt->handle, &a);
727         slaveW =  w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
728         slaveH =  h - a.height + 13;
729    }
730   gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
731 }
732
733 #ifdef __APPLE__
734 static char clickedFile[MSG_SIZ];
735 static int suppress;
736
737 static gboolean
738 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
739 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
740   if(suppress) { // we just started XBoard without arguments
741     strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
742   } else {       // we are running something presumably useful
743     char buf[MSG_SIZ];
744     snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
745     system(buf); // start new instance on this file
746   }
747   return TRUE;
748 }
749 #endif
750
751 int
752 main (int argc, char **argv)
753 {
754     int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
755     int boardWidth, boardHeight, w, h;
756     char *p;
757     int forceMono = False;
758
759     srandom(time(0)); // [HGM] book: make random truly random
760
761     setbuf(stdout, NULL);
762     setbuf(stderr, NULL);
763     debugFP = stderr;
764
765     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
766         printf("%s version %s\n\n  configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
767         exit(0);
768     }
769
770     if(argc > 1 && !strcmp(argv[1], "--help" )) {
771         PrintOptions();
772         exit(0);
773     }
774
775     /* set up GTK */
776     gtk_init (&argc, &argv);
777 #ifdef __APPLE__
778     {   // prepare to catch OX OpenFile signal, which will tell us the clicked file
779         GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
780         char *path = gtkosx_application_get_bundle_path();
781         strncpy(dataDir, path, MSG_SIZ);
782         snprintf(masterSettings, MSG_SIZ, "%s/../Resources/etc/xboard.conf", path);
783         g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
784         // we must call application ready before we can get the signal,
785         // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
786         gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
787         gtkosx_application_ready(theApp);
788         suppress = (argc == 1 || argc > 1 && argv[1][00] != '-'); // OSX sends signal even if name was already argv[1]!
789         if(argc == 1) {                  // called without args: OSX open-file signal might follow
790             static char *fakeArgv[3] = {NULL, clickedFile, NULL};
791             usleep(10000);               // wait 10 msec (and hope this is long enough).
792             while(gtk_events_pending())
793                 gtk_main_iteration();    // process all events that came in upto now
794             suppress = 0;                // future open-file signals should start new instance
795             if(clickedFile[0]) {         // we were sent an open-file signal with filename!
796               fakeArgv[0] = argv[0];
797               argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
798             }
799         }
800     }
801 #endif
802
803     /* set up keyboard accelerators group */
804     GtkAccelerators = gtk_accel_group_new();
805
806     programName = strrchr(argv[0], '/');
807     if (programName == NULL)
808       programName = argv[0];
809     else
810       programName++;
811
812 #ifdef ENABLE_NLS
813 //    if (appData.debugMode) {
814 //      fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
815 //    }
816
817     bindtextdomain(PACKAGE, LOCALEDIR);
818     bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
819     textdomain(PACKAGE);
820 #endif
821
822     appData.boardSize = "";
823     InitAppData(ConvertToLine(argc, argv));
824     p = getenv("HOME");
825     if (p == NULL) p = "/tmp";
826     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
827     gameCopyFilename = (char*) malloc(i);
828     gamePasteFilename = (char*) malloc(i);
829     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
830     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
831
832     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
833         static char buf[MSG_SIZ];
834         EscapeExpand(buf, appData.firstInitString);
835         appData.firstInitString = strdup(buf);
836         EscapeExpand(buf, appData.secondInitString);
837         appData.secondInitString = strdup(buf);
838         EscapeExpand(buf, appData.firstComputerString);
839         appData.firstComputerString = strdup(buf);
840         EscapeExpand(buf, appData.secondComputerString);
841         appData.secondComputerString = strdup(buf);
842     }
843
844     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
845         chessDir = ".";
846     } else {
847         if (chdir(chessDir) != 0) {
848             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
849             perror(chessDir);
850             exit(1);
851         }
852     }
853
854     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
855         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
856         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
857            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
858            exit(errno);
859         }
860         setbuf(debugFP, NULL);
861     }
862
863 #if ENABLE_NLS
864     if (appData.debugMode) {
865       fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
866     }
867 #endif
868
869     /* [HGM,HR] make sure board size is acceptable */
870     if(appData.NrFiles > BOARD_FILES ||
871        appData.NrRanks > BOARD_RANKS   )
872          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
873
874 #if !HIGHDRAG
875     /* This feature does not work; animation needs a rewrite */
876     appData.highlightDragging = FALSE;
877 #endif
878     InitBackEnd1();
879
880         gameInfo.variant = StringToVariant(appData.variant);
881         InitPosition(FALSE);
882
883     /*
884      * determine size, based on supplied or remembered -size, or screen size
885      */
886     if (isdigit(appData.boardSize[0])) {
887         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
888                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
889                    &fontPxlSize, &smallLayout, &tinyLayout);
890         if (i == 0) {
891             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
892                     programName, appData.boardSize);
893             exit(2);
894         }
895         if (i < 7) {
896             /* Find some defaults; use the nearest known size */
897             SizeDefaults *szd, *nearest;
898             int distance = 99999;
899             nearest = szd = sizeDefaults;
900             while (szd->name != NULL) {
901                 if (abs(szd->squareSize - squareSize) < distance) {
902                     nearest = szd;
903                     distance = abs(szd->squareSize - squareSize);
904                     if (distance == 0) break;
905                 }
906                 szd++;
907             }
908             if (i < 2) lineGap = nearest->lineGap;
909             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
910             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
911             if (i < 5) fontPxlSize = nearest->fontPxlSize;
912             if (i < 6) smallLayout = nearest->smallLayout;
913             if (i < 7) tinyLayout = nearest->tinyLayout;
914         }
915     } else {
916         SizeDefaults *szd = sizeDefaults;
917         if (*appData.boardSize == NULLCHAR) {
918             GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
919             guint screenwidth = gdk_screen_get_width(screen);
920             guint screenheight = gdk_screen_get_height(screen);
921             while (screenwidth < szd->minScreenSize ||
922                    screenheight < szd->minScreenSize) {
923               szd++;
924             }
925             if (szd->name == NULL) szd--;
926             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
927         } else {
928             while (szd->name != NULL &&
929                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
930             if (szd->name == NULL) {
931                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
932                         programName, appData.boardSize);
933                 exit(2);
934             }
935         }
936         squareSize = szd->squareSize;
937         lineGap = szd->lineGap;
938         clockFontPxlSize = szd->clockFontPxlSize;
939         coordFontPxlSize = szd->coordFontPxlSize;
940         fontPxlSize = szd->fontPxlSize;
941         smallLayout = szd->smallLayout;
942         tinyLayout = szd->tinyLayout;
943         // [HGM] font: use defaults from settings file if available and not overruled
944     }
945
946     defaultLineGap = lineGap;
947     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
948
949     /* [HR] height treated separately (hacked) */
950     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
951     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
952
953     /*
954      * Determine what fonts to use.
955      */
956 #ifdef TODO_GTK
957     InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
958 #endif
959
960     /*
961      * Detect if there are not enough colors available and adapt.
962      */
963 #ifdef TODO_GTK
964     if (DefaultDepth(xDisplay, xScreen) <= 2) {
965       appData.monoMode = True;
966     }
967 #endif
968
969     forceMono = MakeColors();
970
971     if (forceMono) {
972       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
973               programName);
974         appData.monoMode = True;
975     }
976
977     ParseIcsTextColors();
978
979     /*
980      * widget hierarchy
981      */
982     if (tinyLayout) {
983         layoutName = "tinyLayout";
984     } else if (smallLayout) {
985         layoutName = "smallLayout";
986     } else {
987         layoutName = "normalLayout";
988     }
989
990     wpMain.width = -1; // prevent popup sizes window
991     optList = BoardPopUp(squareSize, lineGap, (void*)
992 #ifdef TODO_GTK
993 #if ENABLE_NLS
994                                                 &clockFontSet);
995 #else
996                                                 clockFontStruct);
997 #endif
998 #else
999 0);
1000 #endif
1001     InitDrawingHandle(optList + W_BOARD);
1002     shellWidget      = shells[BoardWindow];
1003     currBoard        = &optList[W_BOARD];
1004     boardWidget      = optList[W_BOARD].handle;
1005     menuBarWidget    = optList[W_MENU].handle;
1006     dropMenu         = optList[W_DROP].handle;
1007     titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1008 #ifdef TODO_GTK
1009     formWidget  = XtParent(boardWidget);
1010     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1011     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1012     XtGetValues(optList[W_WHITE].handle, args, 2);
1013     if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1014       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1015       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1016       XtGetValues(optList[W_PAUSE].handle, args, 2);
1017     }
1018 #endif
1019
1020     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1021     //       not need to go into InitDrawingSizes().
1022
1023     InitMenuMarkers();
1024
1025     // add accelerators to main shell
1026     gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1027
1028     /*
1029      * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1030      */
1031     WhiteIcon  = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
1032     BlackIcon  = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
1033     mainwindowIcon = WhiteIcon;
1034     gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1035
1036
1037     /*
1038      * Create a cursor for the board widget.
1039      */
1040 #ifdef TODO_GTK
1041     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1042     XChangeWindowAttributes(xDisplay, xBoardWindow,
1043                             CWCursor, &window_attributes);
1044 #endif
1045
1046     /*
1047      * Inhibit shell resizing.
1048      */
1049 #ifdef TODO_GTK
1050     shellArgs[0].value = (XtArgVal) &w;
1051     shellArgs[1].value = (XtArgVal) &h;
1052     XtGetValues(shellWidget, shellArgs, 2);
1053     shellArgs[4].value = shellArgs[2].value = w;
1054     shellArgs[5].value = shellArgs[3].value = h;
1055 //    XtSetValues(shellWidget, &shellArgs[2], 4);
1056 #endif
1057     {
1058         // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1059         // It wil only become known asynchronously, when we first write a string into it.
1060         // This will then change the clock widget height, which triggers resizing the top-level window
1061         // and a configure event. Only then can we know the total height of the top-level window,
1062         // and calculate the height we need. The clockKludge flag suppresses all resizing until
1063         // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1064         int hc;
1065         GtkAllocation a;
1066         gtk_widget_get_allocation(shells[BoardWindow], &a);
1067         w = a.width; h = a.height;
1068         gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1069         clockKludge = hc = a.height;
1070         gtk_widget_get_allocation(boardWidget, &a);
1071         marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1072         marginH =  h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1073     }
1074
1075     CreateAnyPieces();
1076     CreateGrid();
1077
1078     if(appData.logoSize)
1079     {   // locate and read user logo
1080         char buf[MSG_SIZ];
1081         snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1082         ASSIGN(userLogo, buf);
1083     }
1084
1085     if (appData.animate || appData.animateDragging)
1086       CreateAnimVars();
1087
1088     g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1089     g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1090
1091     /* [AS] Restore layout */
1092     if( wpMoveHistory.visible ) {
1093       HistoryPopUp();
1094     }
1095
1096     if( wpEvalGraph.visible )
1097       {
1098         EvalGraphPopUp();
1099       };
1100
1101     if( wpEngineOutput.visible ) {
1102       EngineOutputPopUp();
1103     }
1104
1105     InitBackEnd2();
1106
1107     if (errorExitStatus == -1) {
1108         if (appData.icsActive) {
1109             /* We now wait until we see "login:" from the ICS before
1110                sending the logon script (problems with timestamp otherwise) */
1111             /*ICSInitScript();*/
1112             if (appData.icsInputBox) ICSInputBoxPopUp();
1113         }
1114
1115     #ifdef SIGWINCH
1116     signal(SIGWINCH, TermSizeSigHandler);
1117     #endif
1118         signal(SIGINT, IntSigHandler);
1119         signal(SIGTERM, IntSigHandler);
1120         if (*appData.cmailGameName != NULLCHAR) {
1121             signal(SIGUSR1, CmailSigHandler);
1122         }
1123     }
1124
1125     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1126     InitPosition(TRUE);
1127     UpdateLogos(TRUE);
1128 //    XtSetKeyboardFocus(shellWidget, formWidget);
1129 #ifdef TODO_GTK
1130     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1131 #endif
1132
1133     /* check for GTK events and process them */
1134 //    gtk_main();
1135 while(1) {
1136 gtk_main_iteration();
1137 }
1138
1139     if (appData.debugMode) fclose(debugFP); // [DM] debug
1140     return 0;
1141 }
1142
1143 RETSIGTYPE
1144 TermSizeSigHandler (int sig)
1145 {
1146     update_ics_width();
1147 }
1148
1149 RETSIGTYPE
1150 IntSigHandler (int sig)
1151 {
1152     ExitEvent(sig);
1153 }
1154
1155 RETSIGTYPE
1156 CmailSigHandler (int sig)
1157 {
1158     int dummy = 0;
1159     int error;
1160
1161     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
1162
1163     /* Activate call-back function CmailSigHandlerCallBack()             */
1164     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1165
1166     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1167 }
1168
1169 void
1170 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1171 {
1172     BoardToTop();
1173     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
1174 }
1175 /**** end signal code ****/
1176
1177
1178 #define Abs(n) ((n)<0 ? -(n) : (n))
1179
1180 #ifdef ENABLE_NLS
1181 char *
1182 InsertPxlSize (char *pattern, int targetPxlSize)
1183 {
1184     char *base_fnt_lst, strInt[12], *p, *q;
1185     int alternatives, i, len, strIntLen;
1186
1187     /*
1188      * Replace the "*" (if present) in the pixel-size slot of each
1189      * alternative with the targetPxlSize.
1190      */
1191     p = pattern;
1192     alternatives = 1;
1193     while ((p = strchr(p, ',')) != NULL) {
1194       alternatives++;
1195       p++;
1196     }
1197     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1198     strIntLen = strlen(strInt);
1199     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1200
1201     p = pattern;
1202     q = base_fnt_lst;
1203     while (alternatives--) {
1204       char *comma = strchr(p, ',');
1205       for (i=0; i<14; i++) {
1206         char *hyphen = strchr(p, '-');
1207         if (!hyphen) break;
1208         if (comma && hyphen > comma) break;
1209         len = hyphen + 1 - p;
1210         if (i == 7 && *p == '*' && len == 2) {
1211           p += len;
1212           memcpy(q, strInt, strIntLen);
1213           q += strIntLen;
1214           *q++ = '-';
1215         } else {
1216           memcpy(q, p, len);
1217           p += len;
1218           q += len;
1219         }
1220       }
1221       if (!comma) break;
1222       len = comma + 1 - p;
1223       memcpy(q, p, len);
1224       p += len;
1225       q += len;
1226     }
1227     strcpy(q, p);
1228
1229     return base_fnt_lst;
1230 }
1231
1232 #ifdef TODO_GTK
1233 XFontSet
1234 CreateFontSet (char *base_fnt_lst)
1235 {
1236     XFontSet fntSet;
1237     char **missing_list;
1238     int missing_count;
1239     char *def_string;
1240
1241     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1242                             &missing_list, &missing_count, &def_string);
1243     if (appData.debugMode) {
1244       int i, count;
1245       XFontStruct **font_struct_list;
1246       char **font_name_list;
1247       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1248       if (fntSet) {
1249         fprintf(debugFP, " got list %s, locale %s\n",
1250                 XBaseFontNameListOfFontSet(fntSet),
1251                 XLocaleOfFontSet(fntSet));
1252         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1253         for (i = 0; i < count; i++) {
1254           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1255         }
1256       }
1257       for (i = 0; i < missing_count; i++) {
1258         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1259       }
1260     }
1261     if (fntSet == NULL) {
1262       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1263       exit(2);
1264     }
1265     return fntSet;
1266 }
1267 #endif
1268 #else // not ENABLE_NLS
1269 /*
1270  * Find a font that matches "pattern" that is as close as
1271  * possible to the targetPxlSize.  Prefer fonts that are k
1272  * pixels smaller to fonts that are k pixels larger.  The
1273  * pattern must be in the X Consortium standard format,
1274  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1275  * The return value should be freed with XtFree when no
1276  * longer needed.
1277  */
1278 char *
1279 FindFont (char *pattern, int targetPxlSize)
1280 {
1281     char **fonts, *p, *best, *scalable, *scalableTail;
1282     int i, j, nfonts, minerr, err, pxlSize;
1283
1284 #ifdef TODO_GTK
1285     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1286     if (nfonts < 1) {
1287         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1288                 programName, pattern);
1289         exit(2);
1290     }
1291
1292     best = fonts[0];
1293     scalable = NULL;
1294     minerr = 999999;
1295     for (i=0; i<nfonts; i++) {
1296         j = 0;
1297         p = fonts[i];
1298         if (*p != '-') continue;
1299         while (j < 7) {
1300             if (*p == NULLCHAR) break;
1301             if (*p++ == '-') j++;
1302         }
1303         if (j < 7) continue;
1304         pxlSize = atoi(p);
1305         if (pxlSize == 0) {
1306             scalable = fonts[i];
1307             scalableTail = p;
1308         } else {
1309             err = pxlSize - targetPxlSize;
1310             if (Abs(err) < Abs(minerr) ||
1311                 (minerr > 0 && err < 0 && -err == minerr)) {
1312                 best = fonts[i];
1313                 minerr = err;
1314             }
1315         }
1316     }
1317     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1318         /* If the error is too big and there is a scalable font,
1319            use the scalable font. */
1320         int headlen = scalableTail - scalable;
1321         p = (char *) XtMalloc(strlen(scalable) + 10);
1322         while (isdigit(*scalableTail)) scalableTail++;
1323         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1324     } else {
1325         p = (char *) XtMalloc(strlen(best) + 2);
1326         safeStrCpy(p, best, strlen(best)+1 );
1327     }
1328     if (appData.debugMode) {
1329         fprintf(debugFP, "resolved %s at pixel size %d\n  to %s\n",
1330                 pattern, targetPxlSize, p);
1331     }
1332     XFreeFontNames(fonts);
1333 #endif
1334     return p;
1335 }
1336 #endif
1337
1338 void
1339 EnableNamedMenuItem (char *menuRef, int state)
1340 {
1341     MenuItem *item = MenuNameToItem(menuRef);
1342
1343     if(item) gtk_widget_set_sensitive(item->handle, state);
1344 }
1345
1346 void
1347 EnableButtonBar (int state)
1348 {
1349 #ifdef TODO_GTK
1350     XtSetSensitive(optList[W_BUTTON].handle, state);
1351 #endif
1352 }
1353
1354
1355 void
1356 SetMenuEnables (Enables *enab)
1357 {
1358   while (enab->name != NULL) {
1359     EnableNamedMenuItem(enab->name, enab->value);
1360     enab++;
1361   }
1362 }
1363
1364 gboolean KeyPressProc(window, eventkey, data)
1365      GtkWindow *window;
1366      GdkEventKey  *eventkey;
1367      gpointer data;
1368 {
1369
1370     MoveTypeInProc(eventkey); // pop up for typed in moves
1371
1372 #ifdef TODO_GTK
1373     /* check for other key values */
1374     switch(eventkey->keyval) {
1375         case GDK_question:
1376           AboutGameEvent();
1377           break;
1378         default:
1379           break;
1380     }
1381 #endif
1382     return False;
1383 }
1384 #ifdef TODO_GTK
1385 void
1386 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1387 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1388     MenuItem *item;
1389     if(*nprms == 0) return;
1390     item = MenuNameToItem(prms[0]);
1391     if(item) ((MenuProc *) item->proc) ();
1392 }
1393 #endif
1394
1395 void
1396 SetupDropMenu ()
1397 {
1398 #ifdef TODO_GTK
1399     int i, j, count;
1400     char label[32];
1401     Arg args[16];
1402     Widget entry;
1403     char* p;
1404
1405     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1406         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1407         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1408                    dmEnables[i].piece);
1409         XtSetSensitive(entry, p != NULL || !appData.testLegality
1410                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1411                                        && !appData.icsActive));
1412         count = 0;
1413         while (p && *p++ == dmEnables[i].piece) count++;
1414         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
1415         j = 0;
1416         XtSetArg(args[j], XtNlabel, label); j++;
1417         XtSetValues(entry, args, j);
1418     }
1419 #endif
1420 }
1421
1422 static void
1423 do_flash_delay (unsigned long msec)
1424 {
1425     TimeDelay(msec);
1426 }
1427
1428 void
1429 FlashDelay (int flash_delay)
1430 {
1431         if(flash_delay) do_flash_delay(flash_delay);
1432 }
1433
1434 double
1435 Fraction (int x, int start, int stop)
1436 {
1437    double f = ((double) x - start)/(stop - start);
1438    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1439    return f;
1440 }
1441
1442 static WindowPlacement wpNew;
1443
1444 void
1445 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1446 {
1447     int touch=0, fudge = 2, f = 2;
1448     GetActualPlacement(sh, wp);
1449     if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x)         < fudge) touch = 1; else // right touch
1450     if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x)            < fudge) touch = 2; else // left touch
1451     if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1452     if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y)    < fudge) touch = 4;      // top touch
1453 //printf("CoDrag: touch = %d x=%d w=%d x2=%d w2=%d fx=%d\n", touch, wpMain.x, wpMain.width, wp->x, wp->width, frameX);
1454     if(!touch ) return; // only windows that touch co-move
1455     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1456         int heightInc = wpNew.height - wpMain.height;
1457         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1458         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1459         wp->y += fracTop * heightInc;
1460         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1461 #ifdef TODO_GTK
1462         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1463 #endif
1464         wp->height += heightInc;
1465     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1466         int widthInc = wpNew.width - wpMain.width;
1467         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1468         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1469         wp->y += fracLeft * widthInc;
1470         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1471 #ifdef TODO_GTK
1472         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1473 #endif
1474         wp->width += widthInc;
1475     }
1476     wp->x += wpNew.x - wpMain.x;
1477     wp->y += wpNew.y - wpMain.y;
1478     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1479     if(touch == 3) wp->y += wpNew.height - wpMain.height;
1480 #ifdef TODO_GTK
1481     XtSetArg(args[j], XtNx, wp->x); j++;
1482     XtSetArg(args[j], XtNy, wp->y); j++;
1483     XtSetValues(sh, args, j);
1484 #endif
1485         gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1486 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1487         gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1488 }
1489
1490 void
1491 ReSize (WindowPlacement *wp)
1492 {
1493         GtkAllocation a;
1494         int sqx, sqy, w, h, hc, lg = lineGap;
1495         gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1496         hc = a.height; // clock height can depend on single / double line clock text!
1497         if(clockKludge == a.height) return; // wait for clock to get final size at startup
1498         if(clockKludge) { // clock height OK now; calculate desired initial board height
1499             clockKludge = 0;
1500             wp->height = BOARD_HEIGHT * (squareSize + lineGap) + lineGap + marginH + hc;
1501         }
1502         if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1503         sqx = (wp->width  - lg - marginW) / BOARD_WIDTH - lg;
1504         sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1505         if(sqy < sqx) sqx = sqy;
1506         if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1507             lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1508             sqx = (wp->width  - lg - marginW) / BOARD_WIDTH - lg;
1509             sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1510             if(sqy < sqx) sqx = sqy;
1511         }
1512         if(sqx != squareSize) {
1513 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1514             squareSize = sqx; // adopt new square size
1515             CreatePNGPieces(); // make newly scaled pieces
1516             InitDrawingSizes(0, 0); // creates grid etc.
1517         } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1518         w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1519         h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1520         if(optList[W_BOARD].max   > w) optList[W_BOARD].max = w;
1521         if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1522 }
1523
1524 static guint delayedDragTag = 0;
1525
1526 void
1527 DragProc ()
1528 {
1529         static int busy;
1530         if(busy) return;
1531
1532         busy = 1;
1533 //      GetActualPlacement(shellWidget, &wpNew);
1534         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1535            wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1536             busy = 0; return; // false alarm
1537         }
1538         ReSize(&wpNew);
1539         if(appData.useStickyWindows) {
1540             if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1541             if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1542             if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1543             if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1544         }
1545         wpMain = wpNew;
1546         DrawPosition(True, NULL);
1547         if(delayedDragTag) g_source_remove(delayedDragTag);
1548         delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1549         busy = 0;
1550 }
1551
1552 void
1553 DelayedDrag ()
1554 {
1555 //printf("old timr = %d\n", delayedDragTag);
1556     if(delayedDragTag) g_source_remove(delayedDragTag);
1557     delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1558 //printf("new timr = %d\n", delayedDragTag);
1559 }
1560
1561 static gboolean
1562 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1563 {
1564 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1565     // immediately
1566     wpNew.x = event->configure.x;
1567     wpNew.y = event->configure.y;
1568     wpNew.width  = event->configure.width;
1569     wpNew.height = event->configure.height;
1570     DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1571     return FALSE;
1572 }
1573
1574
1575
1576 /* Disable all user input other than deleting the window */
1577 static int frozen = 0;
1578
1579 void
1580 FreezeUI ()
1581 {
1582   if (frozen) return;
1583   /* Grab by a widget that doesn't accept input */
1584   gtk_grab_add(optList[W_MESSG].handle);
1585   frozen = 1;
1586 }
1587
1588 /* Undo a FreezeUI */
1589 void
1590 ThawUI ()
1591 {
1592   if (!frozen) return;
1593   gtk_grab_remove(optList[W_MESSG].handle);
1594   frozen = 0;
1595 }
1596
1597 void
1598 ModeHighlight ()
1599 {
1600     static int oldPausing = FALSE;
1601     static GameMode oldmode = (GameMode) -1;
1602     char *wname;
1603     if (!boardWidget) return;
1604
1605     if (pausing != oldPausing) {
1606         oldPausing = pausing;
1607         MarkMenuItem("Mode.Pause", pausing);
1608
1609         if (appData.showButtonBar) {
1610           /* Always toggle, don't set.  Previous code messes up when
1611              invoked while the button is pressed, as releasing it
1612              toggles the state again. */
1613             GdkColor color;
1614             gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1615             gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1616         }
1617     }
1618
1619     wname = ModeToWidgetName(oldmode);
1620     if (wname != NULL) {
1621         MarkMenuItem(wname, False);
1622     }
1623     wname = ModeToWidgetName(gameMode);
1624     if (wname != NULL) {
1625         MarkMenuItem(wname, True);
1626     }
1627     oldmode = gameMode;
1628     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1629
1630     /* Maybe all the enables should be handled here, not just this one */
1631     EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1632
1633     DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1634 }
1635
1636
1637 /*
1638  * Button/menu procedures
1639  */
1640
1641 void CopyFileToClipboard(gchar *filename)
1642 {
1643     gchar *selection_tmp;
1644     GtkClipboard *cb;
1645
1646     // read the file
1647     FILE* f = fopen(filename, "r");
1648     long len;
1649     size_t count;
1650     if (f == NULL) return;
1651     fseek(f, 0, 2);
1652     len = ftell(f);
1653     rewind(f);
1654     selection_tmp = g_try_malloc(len + 1);
1655     if (selection_tmp == NULL) {
1656         printf("Malloc failed in CopyFileToClipboard\n");
1657         return;
1658     }
1659     count = fread(selection_tmp, 1, len, f);
1660     fclose(f);
1661     if (len != count) {
1662       g_free(selection_tmp);
1663       return;
1664     }
1665     selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1666
1667     // copy selection_tmp to clipboard
1668     GdkDisplay *gdisp = gdk_display_get_default();
1669     if (!gdisp) {
1670         g_free(selection_tmp);
1671         return;
1672     }
1673     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1674     gtk_clipboard_set_text(cb, selection_tmp, -1);
1675     g_free(selection_tmp);
1676 }
1677
1678 void
1679 CopySomething (char *src)
1680 {
1681     GdkDisplay *gdisp = gdk_display_get_default();
1682     GtkClipboard *cb;
1683     if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1684     if (gdisp == NULL) return;
1685     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1686     gtk_clipboard_set_text(cb, src, -1);
1687 }
1688
1689 void
1690 PastePositionProc ()
1691 {
1692     GdkDisplay *gdisp = gdk_display_get_default();
1693     GtkClipboard *cb;
1694     gchar *fenstr;
1695
1696     if (gdisp == NULL) return;
1697     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1698     fenstr = gtk_clipboard_wait_for_text(cb);
1699     if (fenstr==NULL) return; // nothing had been selected to copy
1700     EditPositionPasteFEN(fenstr);
1701     return;
1702 }
1703
1704 void
1705 PasteGameProc ()
1706 {
1707     gchar *text=NULL;
1708     GtkClipboard *cb;
1709     guint len=0;
1710     FILE* f;
1711
1712     // get game from clipboard
1713     GdkDisplay *gdisp = gdk_display_get_default();
1714     if (gdisp == NULL) return;
1715     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1716     text = gtk_clipboard_wait_for_text(cb);
1717     if (text == NULL) return; // nothing to paste
1718     len = strlen(text);
1719
1720     // write to temp file
1721     if (text == NULL || len == 0) {
1722       return; //nothing to paste
1723     }
1724     f = fopen(gamePasteFilename, "w");
1725     if (f == NULL) {
1726       DisplayError(_("Can't open temp file"), errno);
1727       return;
1728     }
1729     fwrite(text, 1, len, f);
1730     fclose(f);
1731
1732     // load from file
1733     LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1734     return;
1735 }
1736
1737
1738 #ifdef TODO_GTK
1739 void
1740 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1741 {
1742     QuitProc();
1743 }
1744 #endif
1745
1746 void MoveTypeInProc(eventkey)
1747     GdkEventKey  *eventkey;
1748 {
1749     char buf[10];
1750
1751     // ingnore if ctrl, alt, or meta is pressed
1752     if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1753         return;
1754     }
1755
1756     buf[0]=eventkey->keyval;
1757     buf[1]='\0';
1758     if (eventkey->keyval > 32 && eventkey->keyval < 256)
1759         BoxAutoPopUp (buf);
1760 }
1761
1762 #ifdef TODO_GTK
1763 void
1764 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1765 {
1766         if (!TempBackwardActive) {
1767                 TempBackwardActive = True;
1768                 BackwardEvent();
1769         }
1770 }
1771
1772 void
1773 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1774 {
1775         /* Check to see if triggered by a key release event for a repeating key.
1776          * If so the next queued event will be a key press of the same key at the same time */
1777         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1778                 XEvent next;
1779                 XPeekEvent(xDisplay, &next);
1780                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1781                         next.xkey.keycode == event->xkey.keycode)
1782                                 return;
1783         }
1784     ForwardEvent();
1785         TempBackwardActive = False;
1786 }
1787 #endif
1788
1789 void
1790 ManProc ()
1791 {   // called from menu
1792 #ifdef __APPLE__
1793     system("%s ./man.command", appData.sysOpen);
1794 #else
1795     system("xterm -e man xboard &");
1796 #endif
1797 }
1798
1799 void
1800 SetWindowTitle (char *text, char *title, char *icon)
1801 {
1802 #ifdef TODO_GTK
1803     Arg args[16];
1804     int i;
1805     if (appData.titleInWindow) {
1806         i = 0;
1807         XtSetArg(args[i], XtNlabel, text);   i++;
1808         XtSetValues(titleWidget, args, i);
1809     }
1810     i = 0;
1811     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
1812     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
1813     XtSetValues(shellWidget, args, i);
1814     XSync(xDisplay, False);
1815 #endif
1816     if (appData.titleInWindow) {
1817         SetWidgetLabel(titleWidget, text);
1818     }
1819     gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1820 }
1821
1822
1823 void
1824 DisplayIcsInteractionTitle (String message)
1825 {
1826 #ifdef TODO_GTK
1827   if (oldICSInteractionTitle == NULL) {
1828     /* Magic to find the old window title, adapted from vim */
1829     char *wina = getenv("WINDOWID");
1830     if (wina != NULL) {
1831       Window win = (Window) atoi(wina);
1832       Window root, parent, *children;
1833       unsigned int nchildren;
1834       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1835       for (;;) {
1836         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1837         if (!XQueryTree(xDisplay, win, &root, &parent,
1838                         &children, &nchildren)) break;
1839         if (children) XFree((void *)children);
1840         if (parent == root || parent == 0) break;
1841         win = parent;
1842       }
1843       XSetErrorHandler(oldHandler);
1844     }
1845     if (oldICSInteractionTitle == NULL) {
1846       oldICSInteractionTitle = "xterm";
1847     }
1848   }
1849   printf("\033]0;%s\007", message);
1850   fflush(stdout);
1851 #endif
1852 }
1853
1854
1855 void
1856 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1857 {
1858     GtkWidget *w = (GtkWidget *) opt->handle;
1859     GdkColor col;
1860     char *markup;
1861     char bgcolor[10];
1862     char fgcolor[10];
1863
1864     if (highlight) {
1865         strcpy(bgcolor, "black");
1866         strcpy(fgcolor, "white");
1867     } else {
1868         strcpy(bgcolor, "white");
1869         strcpy(fgcolor, "black");
1870     }
1871     if (timer > 0 &&
1872         appData.lowTimeWarning &&
1873         (timer / 1000) < appData.icsAlarmTime) {
1874         strcpy(fgcolor, appData.lowTimeWarningColor);
1875     }
1876
1877     gdk_color_parse( bgcolor, &col );
1878     gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1879
1880     if (appData.clockMode) {
1881         markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1882                                          bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1883     } else {
1884         markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s  </span>",
1885                                          bgcolor, fgcolor, color);
1886     }
1887     gtk_label_set_markup(GTK_LABEL(w), markup);
1888     g_free(markup);
1889 }
1890
1891 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1892
1893 void
1894 SetClockIcon (int color)
1895 {
1896     GdkPixbuf *pm = *clockIcons[color];
1897     if (mainwindowIcon != pm) {
1898         mainwindowIcon = pm;
1899         gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1900     }
1901 }
1902
1903 #define INPUT_SOURCE_BUF_SIZE 8192
1904
1905 typedef struct {
1906     CPKind kind;
1907     int fd;
1908     int lineByLine;
1909     char *unused;
1910     InputCallback func;
1911     guint sid;
1912     char buf[INPUT_SOURCE_BUF_SIZE];
1913     VOIDSTAR closure;
1914 } InputSource;
1915
1916 gboolean
1917 DoInputCallback(io, cond, data)
1918      GIOChannel  *io;
1919      GIOCondition cond;
1920      gpointer    *data;
1921 {
1922   /* read input from one of the input source (for example a chess program, ICS, etc).
1923    * and call a function that will handle the input
1924    */
1925
1926     int count;
1927     int error;
1928     char *p, *q;
1929
1930     /* All information (callback function, file descriptor, etc) is
1931      * saved in an InputSource structure
1932      */
1933     InputSource *is = (InputSource *) data;
1934
1935     if (is->lineByLine) {
1936         count = read(is->fd, is->unused,
1937                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1938         if (count <= 0) {
1939             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1940             return True;
1941         }
1942         is->unused += count;
1943         p = is->buf;
1944         /* break input into lines and call the callback function on each
1945          * line
1946          */
1947         while (p < is->unused) {
1948             q = memchr(p, '\n', is->unused - p);
1949             if (q == NULL) break;
1950             q++;
1951             (is->func)(is, is->closure, p, q - p, 0);
1952             p = q;
1953         }
1954         /* remember not yet used part of the buffer */
1955         q = is->buf;
1956         while (p < is->unused) {
1957             *q++ = *p++;
1958         }
1959         is->unused = q;
1960     } else {
1961       /* read maximum length of input buffer and send the whole buffer
1962        * to the callback function
1963        */
1964         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1965         if (count == -1)
1966           error = errno;
1967         else
1968           error = 0;
1969         (is->func)(is, is->closure, is->buf, count, error);
1970     }
1971     return True; // Must return true or the watch will be removed
1972 }
1973
1974 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1975      ProcRef pr;
1976      int lineByLine;
1977      InputCallback func;
1978      VOIDSTAR closure;
1979 {
1980     InputSource *is;
1981     GIOChannel *channel;
1982     ChildProc *cp = (ChildProc *) pr;
1983
1984     is = (InputSource *) calloc(1, sizeof(InputSource));
1985     is->lineByLine = lineByLine;
1986     is->func = func;
1987     if (pr == NoProc) {
1988         is->kind = CPReal;
1989         is->fd = fileno(stdin);
1990     } else {
1991         is->kind = cp->kind;
1992         is->fd = cp->fdFrom;
1993     }
1994     if (lineByLine)
1995       is->unused = is->buf;
1996     else
1997       is->unused = NULL;
1998
1999    /* GTK-TODO: will this work on windows?*/
2000
2001     channel = g_io_channel_unix_new(is->fd);
2002     g_io_channel_set_close_on_unref (channel, TRUE);
2003     is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2004
2005     is->closure = closure;
2006     return (InputSourceRef) is;
2007 }
2008
2009
2010 void
2011 RemoveInputSource(isr)
2012      InputSourceRef isr;
2013 {
2014     InputSource *is = (InputSource *) isr;
2015
2016     if (is->sid == 0) return;
2017     g_source_remove(is->sid);
2018     is->sid = 0;
2019     return;
2020 }
2021
2022 #ifndef HAVE_USLEEP
2023
2024 static Boolean frameWaiting;
2025
2026 static RETSIGTYPE
2027 FrameAlarm (int sig)
2028 {
2029   frameWaiting = False;
2030   /* In case System-V style signals.  Needed?? */
2031   signal(SIGALRM, FrameAlarm);
2032 }
2033
2034 void
2035 FrameDelay (int time)
2036 {
2037   struct itimerval delay;
2038
2039   if (time > 0) {
2040     frameWaiting = True;
2041     signal(SIGALRM, FrameAlarm);
2042     delay.it_interval.tv_sec =
2043       delay.it_value.tv_sec = time / 1000;
2044     delay.it_interval.tv_usec =
2045       delay.it_value.tv_usec = (time % 1000) * 1000;
2046     setitimer(ITIMER_REAL, &delay, NULL);
2047     while (frameWaiting) pause();
2048     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2049     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2050     setitimer(ITIMER_REAL, &delay, NULL);
2051   }
2052 }
2053
2054 #else
2055
2056 void
2057 FrameDelay (int time)
2058 {
2059 #ifdef TODO_GTK
2060   XSync(xDisplay, False);
2061 #endif
2062 //  gtk_main_iteration_do(False);
2063
2064   if (time > 0)
2065     usleep(time * 1000);
2066 }
2067
2068 #endif
2069
2070 static void
2071 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2072 {
2073     char buf[MSG_SIZ], *logoName = buf;
2074     if(appData.logo[n][0]) {
2075         logoName = appData.logo[n];
2076     } else if(appData.autoLogo) {
2077         if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2078             sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2079         } else if(appData.directory[n] && appData.directory[n][0]) {
2080             sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2081         }
2082     }
2083     if(logoName[0])
2084         { ASSIGN(cps->programLogo, logoName); }
2085 }
2086
2087 void
2088 UpdateLogos (int displ)
2089 {
2090     if(optList[W_WHITE-1].handle == NULL) return;
2091     LoadLogo(&first, 0, 0);
2092     LoadLogo(&second, 1, appData.icsActive);
2093     if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2094     return;
2095 }
2096
2097 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2098      char *label;
2099      char *def;
2100      char *filter;
2101      FileProc proc;
2102      char *openMode;
2103      Boolean pathFlag;
2104      char **name;
2105      FILE **fp;
2106 {
2107   GtkWidget     *dialog;
2108   GtkFileFilter *gtkfilter;
2109   GtkFileFilter *gtkfilter_all;
2110   char space[]     = " ";
2111   char fileext[10] = "";
2112   char *result     = NULL;
2113   char *cp;
2114
2115   /* make a copy of the filter string, so that strtok can work with it*/
2116   cp = strdup(filter);
2117
2118   /* add filters for file extensions */
2119   gtkfilter     = gtk_file_filter_new();
2120   gtkfilter_all = gtk_file_filter_new();
2121
2122   /* one filter to show everything */
2123   gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2124   gtk_file_filter_set_name   (gtkfilter_all, "All Files");
2125
2126   /* add filter if present */
2127   result = strtok(cp, space);
2128   while( result != NULL  ) {
2129     snprintf(fileext,10,"*%s",result);
2130     result = strtok( NULL, space );
2131     gtk_file_filter_add_pattern(gtkfilter, fileext);
2132   };
2133
2134   /* second filter to only show what's useful */
2135   gtk_file_filter_set_name (gtkfilter,filter);
2136
2137   if (openMode[0] == 'r')
2138     {
2139       dialog = gtk_file_chooser_dialog_new (label,
2140                                             NULL,
2141                                             GTK_FILE_CHOOSER_ACTION_OPEN,
2142                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2143                                             GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2144                                             NULL);
2145     }
2146   else
2147     {
2148       dialog = gtk_file_chooser_dialog_new (label,
2149                                             NULL,
2150                                             GTK_FILE_CHOOSER_ACTION_SAVE,
2151                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2152                                             GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2153                                             NULL);
2154       /* add filename suggestions */
2155       if (strlen(def) > 0 )
2156         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2157
2158       //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2159     }
2160
2161   /* add filters */
2162   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2163   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2164   /* activate filter */
2165   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2166
2167   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2168     {
2169       char *filename;
2170       FILE *f;
2171
2172       filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2173
2174       //see loadgamepopup
2175       f = fopen(filename, openMode);
2176       if (f == NULL)
2177         {
2178           DisplayError(_("Failed to open file"), errno);
2179         }
2180       else
2181         {
2182           /* TODO add indec */
2183             *fp = f;
2184             ASSIGN(*name, filename);
2185             ScheduleDelayedEvent(DelayedLoad, 50);
2186         }
2187       g_free (filename);
2188     };
2189
2190   gtk_widget_destroy (dialog);
2191   ModeHighlight();
2192
2193   free(cp);
2194   return;
2195
2196 }