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