Merge branch 'v4.7.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 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 #  define DATADIR "~~"
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         dataDir = gtkosx_application_get_bundle_path();
781         g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
782         // we must call application ready before we can get the signal,
783         // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
784         gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
785         gtkosx_application_ready(theApp);
786         suppress = (argc == 1 || argc > 1 && argv[1][00] != '-'); // OSX sends signal even if name was already argv[1]!
787         if(argc == 1) {                  // called without args: OSX open-file signal might follow
788             static char *fakeArgv[3] = {NULL, clickedFile, NULL};
789             usleep(10000);               // wait 10 msec (and hope this is long enough).
790             while(gtk_events_pending())
791                 gtk_main_iteration();    // process all events that came in upto now
792             suppress = 0;                // future open-file signals should start new instance
793             if(clickedFile[0]) {         // we were sent an open-file signal with filename!
794               fakeArgv[0] = argv[0];
795               argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
796             }
797         }
798     }
799 #endif
800
801     /* set up keyboard accelerators group */
802     GtkAccelerators = gtk_accel_group_new();
803
804     programName = strrchr(argv[0], '/');
805     if (programName == NULL)
806       programName = argv[0];
807     else
808       programName++;
809
810 #ifdef ENABLE_NLS
811 //    if (appData.debugMode) {
812 //      fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
813 //    }
814
815     bindtextdomain(PACKAGE, LOCALEDIR);
816     bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
817     textdomain(PACKAGE);
818 #endif
819
820     appData.boardSize = "";
821     InitAppData(ConvertToLine(argc, argv));
822     p = getenv("HOME");
823     if (p == NULL) p = "/tmp";
824     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
825     gameCopyFilename = (char*) malloc(i);
826     gamePasteFilename = (char*) malloc(i);
827     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
828     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
829
830     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
831         static char buf[MSG_SIZ];
832         EscapeExpand(buf, appData.firstInitString);
833         appData.firstInitString = strdup(buf);
834         EscapeExpand(buf, appData.secondInitString);
835         appData.secondInitString = strdup(buf);
836         EscapeExpand(buf, appData.firstComputerString);
837         appData.firstComputerString = strdup(buf);
838         EscapeExpand(buf, appData.secondComputerString);
839         appData.secondComputerString = strdup(buf);
840     }
841
842     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
843         chessDir = ".";
844     } else {
845         if (chdir(chessDir) != 0) {
846             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
847             perror(chessDir);
848             exit(1);
849         }
850     }
851
852     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
853         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
854         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
855            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
856            exit(errno);
857         }
858         setbuf(debugFP, NULL);
859     }
860
861 #if ENABLE_NLS
862     if (appData.debugMode) {
863       fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
864     }
865 #endif
866
867     /* [HGM,HR] make sure board size is acceptable */
868     if(appData.NrFiles > BOARD_FILES ||
869        appData.NrRanks > BOARD_RANKS   )
870          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
871
872 #if !HIGHDRAG
873     /* This feature does not work; animation needs a rewrite */
874     appData.highlightDragging = FALSE;
875 #endif
876     InitBackEnd1();
877
878         gameInfo.variant = StringToVariant(appData.variant);
879         InitPosition(FALSE);
880
881     /*
882      * determine size, based on supplied or remembered -size, or screen size
883      */
884     if (isdigit(appData.boardSize[0])) {
885         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
886                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
887                    &fontPxlSize, &smallLayout, &tinyLayout);
888         if (i == 0) {
889             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
890                     programName, appData.boardSize);
891             exit(2);
892         }
893         if (i < 7) {
894             /* Find some defaults; use the nearest known size */
895             SizeDefaults *szd, *nearest;
896             int distance = 99999;
897             nearest = szd = sizeDefaults;
898             while (szd->name != NULL) {
899                 if (abs(szd->squareSize - squareSize) < distance) {
900                     nearest = szd;
901                     distance = abs(szd->squareSize - squareSize);
902                     if (distance == 0) break;
903                 }
904                 szd++;
905             }
906             if (i < 2) lineGap = nearest->lineGap;
907             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
908             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
909             if (i < 5) fontPxlSize = nearest->fontPxlSize;
910             if (i < 6) smallLayout = nearest->smallLayout;
911             if (i < 7) tinyLayout = nearest->tinyLayout;
912         }
913     } else {
914         SizeDefaults *szd = sizeDefaults;
915         if (*appData.boardSize == NULLCHAR) {
916             GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
917             guint screenwidth = gdk_screen_get_width(screen);
918             guint screenheight = gdk_screen_get_height(screen);
919             while (screenwidth < szd->minScreenSize ||
920                    screenheight < szd->minScreenSize) {
921               szd++;
922             }
923             if (szd->name == NULL) szd--;
924             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
925         } else {
926             while (szd->name != NULL &&
927                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
928             if (szd->name == NULL) {
929                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
930                         programName, appData.boardSize);
931                 exit(2);
932             }
933         }
934         squareSize = szd->squareSize;
935         lineGap = szd->lineGap;
936         clockFontPxlSize = szd->clockFontPxlSize;
937         coordFontPxlSize = szd->coordFontPxlSize;
938         fontPxlSize = szd->fontPxlSize;
939         smallLayout = szd->smallLayout;
940         tinyLayout = szd->tinyLayout;
941         // [HGM] font: use defaults from settings file if available and not overruled
942     }
943
944     defaultLineGap = lineGap;
945     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
946
947     /* [HR] height treated separately (hacked) */
948     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
949     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
950
951     /*
952      * Determine what fonts to use.
953      */
954 #ifdef TODO_GTK
955     InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
956 #endif
957
958     /*
959      * Detect if there are not enough colors available and adapt.
960      */
961 #ifdef TODO_GTK
962     if (DefaultDepth(xDisplay, xScreen) <= 2) {
963       appData.monoMode = True;
964     }
965 #endif
966
967     forceMono = MakeColors();
968
969     if (forceMono) {
970       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
971               programName);
972         appData.monoMode = True;
973     }
974
975     ParseIcsTextColors();
976
977     /*
978      * widget hierarchy
979      */
980     if (tinyLayout) {
981         layoutName = "tinyLayout";
982     } else if (smallLayout) {
983         layoutName = "smallLayout";
984     } else {
985         layoutName = "normalLayout";
986     }
987
988     wpMain.width = -1; // prevent popup sizes window
989     optList = BoardPopUp(squareSize, lineGap, (void*)
990 #ifdef TODO_GTK
991 #if ENABLE_NLS
992                                                 &clockFontSet);
993 #else
994                                                 clockFontStruct);
995 #endif
996 #else
997 0);
998 #endif
999     InitDrawingHandle(optList + W_BOARD);
1000     shellWidget      = shells[BoardWindow];
1001     currBoard        = &optList[W_BOARD];
1002     boardWidget      = optList[W_BOARD].handle;
1003     menuBarWidget    = optList[W_MENU].handle;
1004     dropMenu         = optList[W_DROP].handle;
1005     titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1006 #ifdef TODO_GTK
1007     formWidget  = XtParent(boardWidget);
1008     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1009     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1010     XtGetValues(optList[W_WHITE].handle, args, 2);
1011     if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1012       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1013       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1014       XtGetValues(optList[W_PAUSE].handle, args, 2);
1015     }
1016 #endif
1017
1018     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1019     //       not need to go into InitDrawingSizes().
1020
1021     InitMenuMarkers();
1022
1023     // add accelerators to main shell
1024     gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1025
1026     /*
1027      * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1028      */
1029     WhiteIcon  = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
1030     BlackIcon  = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
1031     mainwindowIcon = WhiteIcon;
1032     gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1033
1034
1035     /*
1036      * Create a cursor for the board widget.
1037      */
1038 #ifdef TODO_GTK
1039     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1040     XChangeWindowAttributes(xDisplay, xBoardWindow,
1041                             CWCursor, &window_attributes);
1042 #endif
1043
1044     /*
1045      * Inhibit shell resizing.
1046      */
1047 #ifdef TODO_GTK
1048     shellArgs[0].value = (XtArgVal) &w;
1049     shellArgs[1].value = (XtArgVal) &h;
1050     XtGetValues(shellWidget, shellArgs, 2);
1051     shellArgs[4].value = shellArgs[2].value = w;
1052     shellArgs[5].value = shellArgs[3].value = h;
1053 //    XtSetValues(shellWidget, &shellArgs[2], 4);
1054 #endif
1055     {
1056         // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1057         // It wil only become known asynchronously, when we first write a string into it.
1058         // This will then change the clock widget height, which triggers resizing the top-level window
1059         // and a configure event. Only then can we know the total height of the top-level window,
1060         // and calculate the height we need. The clockKludge flag suppresses all resizing until
1061         // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1062         int hc;
1063         GtkAllocation a;
1064         gtk_widget_get_allocation(shells[BoardWindow], &a);
1065         w = a.width; h = a.height;
1066         gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1067         clockKludge = hc = a.height;
1068         gtk_widget_get_allocation(boardWidget, &a);
1069         marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1070         marginH =  h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1071     }
1072
1073     CreateAnyPieces();
1074     CreateGrid();
1075
1076     if(appData.logoSize)
1077     {   // locate and read user logo
1078         char buf[MSG_SIZ];
1079         snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1080         ASSIGN(userLogo, buf);
1081     }
1082
1083     if (appData.animate || appData.animateDragging)
1084       CreateAnimVars();
1085
1086     g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1087     g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1088
1089     /* [AS] Restore layout */
1090     if( wpMoveHistory.visible ) {
1091       HistoryPopUp();
1092     }
1093
1094     if( wpEvalGraph.visible )
1095       {
1096         EvalGraphPopUp();
1097       };
1098
1099     if( wpEngineOutput.visible ) {
1100       EngineOutputPopUp();
1101     }
1102
1103     InitBackEnd2();
1104
1105     if (errorExitStatus == -1) {
1106         if (appData.icsActive) {
1107             /* We now wait until we see "login:" from the ICS before
1108                sending the logon script (problems with timestamp otherwise) */
1109             /*ICSInitScript();*/
1110             if (appData.icsInputBox) ICSInputBoxPopUp();
1111         }
1112
1113     #ifdef SIGWINCH
1114     signal(SIGWINCH, TermSizeSigHandler);
1115     #endif
1116         signal(SIGINT, IntSigHandler);
1117         signal(SIGTERM, IntSigHandler);
1118         if (*appData.cmailGameName != NULLCHAR) {
1119             signal(SIGUSR1, CmailSigHandler);
1120         }
1121     }
1122
1123     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1124     InitPosition(TRUE);
1125     UpdateLogos(TRUE);
1126 //    XtSetKeyboardFocus(shellWidget, formWidget);
1127 #ifdef TODO_GTK
1128     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1129 #endif
1130
1131     /* check for GTK events and process them */
1132 //    gtk_main();
1133 while(1) {
1134 gtk_main_iteration();
1135 }
1136
1137     if (appData.debugMode) fclose(debugFP); // [DM] debug
1138     return 0;
1139 }
1140
1141 RETSIGTYPE
1142 TermSizeSigHandler (int sig)
1143 {
1144     update_ics_width();
1145 }
1146
1147 RETSIGTYPE
1148 IntSigHandler (int sig)
1149 {
1150     ExitEvent(sig);
1151 }
1152
1153 RETSIGTYPE
1154 CmailSigHandler (int sig)
1155 {
1156     int dummy = 0;
1157     int error;
1158
1159     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
1160
1161     /* Activate call-back function CmailSigHandlerCallBack()             */
1162     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1163
1164     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1165 }
1166
1167 void
1168 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1169 {
1170     BoardToTop();
1171     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
1172 }
1173 /**** end signal code ****/
1174
1175
1176 #define Abs(n) ((n)<0 ? -(n) : (n))
1177
1178 #ifdef ENABLE_NLS
1179 char *
1180 InsertPxlSize (char *pattern, int targetPxlSize)
1181 {
1182     char *base_fnt_lst, strInt[12], *p, *q;
1183     int alternatives, i, len, strIntLen;
1184
1185     /*
1186      * Replace the "*" (if present) in the pixel-size slot of each
1187      * alternative with the targetPxlSize.
1188      */
1189     p = pattern;
1190     alternatives = 1;
1191     while ((p = strchr(p, ',')) != NULL) {
1192       alternatives++;
1193       p++;
1194     }
1195     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1196     strIntLen = strlen(strInt);
1197     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1198
1199     p = pattern;
1200     q = base_fnt_lst;
1201     while (alternatives--) {
1202       char *comma = strchr(p, ',');
1203       for (i=0; i<14; i++) {
1204         char *hyphen = strchr(p, '-');
1205         if (!hyphen) break;
1206         if (comma && hyphen > comma) break;
1207         len = hyphen + 1 - p;
1208         if (i == 7 && *p == '*' && len == 2) {
1209           p += len;
1210           memcpy(q, strInt, strIntLen);
1211           q += strIntLen;
1212           *q++ = '-';
1213         } else {
1214           memcpy(q, p, len);
1215           p += len;
1216           q += len;
1217         }
1218       }
1219       if (!comma) break;
1220       len = comma + 1 - p;
1221       memcpy(q, p, len);
1222       p += len;
1223       q += len;
1224     }
1225     strcpy(q, p);
1226
1227     return base_fnt_lst;
1228 }
1229
1230 #ifdef TODO_GTK
1231 XFontSet
1232 CreateFontSet (char *base_fnt_lst)
1233 {
1234     XFontSet fntSet;
1235     char **missing_list;
1236     int missing_count;
1237     char *def_string;
1238
1239     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1240                             &missing_list, &missing_count, &def_string);
1241     if (appData.debugMode) {
1242       int i, count;
1243       XFontStruct **font_struct_list;
1244       char **font_name_list;
1245       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1246       if (fntSet) {
1247         fprintf(debugFP, " got list %s, locale %s\n",
1248                 XBaseFontNameListOfFontSet(fntSet),
1249                 XLocaleOfFontSet(fntSet));
1250         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1251         for (i = 0; i < count; i++) {
1252           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1253         }
1254       }
1255       for (i = 0; i < missing_count; i++) {
1256         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1257       }
1258     }
1259     if (fntSet == NULL) {
1260       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1261       exit(2);
1262     }
1263     return fntSet;
1264 }
1265 #endif
1266 #else // not ENABLE_NLS
1267 /*
1268  * Find a font that matches "pattern" that is as close as
1269  * possible to the targetPxlSize.  Prefer fonts that are k
1270  * pixels smaller to fonts that are k pixels larger.  The
1271  * pattern must be in the X Consortium standard format,
1272  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1273  * The return value should be freed with XtFree when no
1274  * longer needed.
1275  */
1276 char *
1277 FindFont (char *pattern, int targetPxlSize)
1278 {
1279     char **fonts, *p, *best, *scalable, *scalableTail;
1280     int i, j, nfonts, minerr, err, pxlSize;
1281
1282 #ifdef TODO_GTK
1283     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1284     if (nfonts < 1) {
1285         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1286                 programName, pattern);
1287         exit(2);
1288     }
1289
1290     best = fonts[0];
1291     scalable = NULL;
1292     minerr = 999999;
1293     for (i=0; i<nfonts; i++) {
1294         j = 0;
1295         p = fonts[i];
1296         if (*p != '-') continue;
1297         while (j < 7) {
1298             if (*p == NULLCHAR) break;
1299             if (*p++ == '-') j++;
1300         }
1301         if (j < 7) continue;
1302         pxlSize = atoi(p);
1303         if (pxlSize == 0) {
1304             scalable = fonts[i];
1305             scalableTail = p;
1306         } else {
1307             err = pxlSize - targetPxlSize;
1308             if (Abs(err) < Abs(minerr) ||
1309                 (minerr > 0 && err < 0 && -err == minerr)) {
1310                 best = fonts[i];
1311                 minerr = err;
1312             }
1313         }
1314     }
1315     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1316         /* If the error is too big and there is a scalable font,
1317            use the scalable font. */
1318         int headlen = scalableTail - scalable;
1319         p = (char *) XtMalloc(strlen(scalable) + 10);
1320         while (isdigit(*scalableTail)) scalableTail++;
1321         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1322     } else {
1323         p = (char *) XtMalloc(strlen(best) + 2);
1324         safeStrCpy(p, best, strlen(best)+1 );
1325     }
1326     if (appData.debugMode) {
1327         fprintf(debugFP, "resolved %s at pixel size %d\n  to %s\n",
1328                 pattern, targetPxlSize, p);
1329     }
1330     XFreeFontNames(fonts);
1331 #endif
1332     return p;
1333 }
1334 #endif
1335
1336 void
1337 EnableNamedMenuItem (char *menuRef, int state)
1338 {
1339     MenuItem *item = MenuNameToItem(menuRef);
1340
1341     if(item) gtk_widget_set_sensitive(item->handle, state);
1342 }
1343
1344 void
1345 EnableButtonBar (int state)
1346 {
1347 #ifdef TODO_GTK
1348     XtSetSensitive(optList[W_BUTTON].handle, state);
1349 #endif
1350 }
1351
1352
1353 void
1354 SetMenuEnables (Enables *enab)
1355 {
1356   while (enab->name != NULL) {
1357     EnableNamedMenuItem(enab->name, enab->value);
1358     enab++;
1359   }
1360 }
1361
1362 gboolean KeyPressProc(window, eventkey, data)
1363      GtkWindow *window;
1364      GdkEventKey  *eventkey;
1365      gpointer data;
1366 {
1367
1368     MoveTypeInProc(eventkey); // pop up for typed in moves
1369
1370 #ifdef TODO_GTK
1371     /* check for other key values */
1372     switch(eventkey->keyval) {
1373         case GDK_question:
1374           AboutGameEvent();
1375           break;
1376         default:
1377           break;
1378     }
1379 #endif
1380     return False;
1381 }
1382 #ifdef TODO_GTK
1383 void
1384 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1385 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1386     MenuItem *item;
1387     if(*nprms == 0) return;
1388     item = MenuNameToItem(prms[0]);
1389     if(item) ((MenuProc *) item->proc) ();
1390 }
1391 #endif
1392
1393 void
1394 SetupDropMenu ()
1395 {
1396 #ifdef TODO_GTK
1397     int i, j, count;
1398     char label[32];
1399     Arg args[16];
1400     Widget entry;
1401     char* p;
1402
1403     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1404         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1405         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1406                    dmEnables[i].piece);
1407         XtSetSensitive(entry, p != NULL || !appData.testLegality
1408                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1409                                        && !appData.icsActive));
1410         count = 0;
1411         while (p && *p++ == dmEnables[i].piece) count++;
1412         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
1413         j = 0;
1414         XtSetArg(args[j], XtNlabel, label); j++;
1415         XtSetValues(entry, args, j);
1416     }
1417 #endif
1418 }
1419
1420 static void
1421 do_flash_delay (unsigned long msec)
1422 {
1423     TimeDelay(msec);
1424 }
1425
1426 void
1427 FlashDelay (int flash_delay)
1428 {
1429         if(flash_delay) do_flash_delay(flash_delay);
1430 }
1431
1432 double
1433 Fraction (int x, int start, int stop)
1434 {
1435    double f = ((double) x - start)/(stop - start);
1436    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1437    return f;
1438 }
1439
1440 static WindowPlacement wpNew;
1441
1442 void
1443 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1444 {
1445     int touch=0, fudge = 2, f = 2;
1446     GetActualPlacement(sh, wp);
1447     if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x)         < fudge) touch = 1; else // right touch
1448     if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x)            < fudge) touch = 2; else // left touch
1449     if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1450     if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y)    < fudge) touch = 4;      // top touch
1451 //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);
1452     if(!touch ) return; // only windows that touch co-move
1453     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1454         int heightInc = wpNew.height - wpMain.height;
1455         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1456         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1457         wp->y += fracTop * heightInc;
1458         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1459 #ifdef TODO_GTK
1460         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1461 #endif
1462         wp->height += heightInc;
1463     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1464         int widthInc = wpNew.width - wpMain.width;
1465         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1466         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1467         wp->y += fracLeft * widthInc;
1468         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1469 #ifdef TODO_GTK
1470         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1471 #endif
1472         wp->width += widthInc;
1473     }
1474     wp->x += wpNew.x - wpMain.x;
1475     wp->y += wpNew.y - wpMain.y;
1476     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1477     if(touch == 3) wp->y += wpNew.height - wpMain.height;
1478 #ifdef TODO_GTK
1479     XtSetArg(args[j], XtNx, wp->x); j++;
1480     XtSetArg(args[j], XtNy, wp->y); j++;
1481     XtSetValues(sh, args, j);
1482 #endif
1483         gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1484 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1485         gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1486 }
1487
1488 void
1489 ReSize (WindowPlacement *wp)
1490 {
1491         GtkAllocation a;
1492         int sqx, sqy, w, h, hc, lg = lineGap;
1493         gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1494         hc = a.height; // clock height can depend on single / double line clock text!
1495         if(clockKludge == a.height) return; // wait for clock to get final size at startup
1496         if(clockKludge) { // clock height OK now; calculate desired initial board height
1497             clockKludge = 0;
1498             wp->height = BOARD_HEIGHT * (squareSize + lineGap) + lineGap + marginH + hc;
1499         }
1500         if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1501         sqx = (wp->width  - lg - marginW) / BOARD_WIDTH - lg;
1502         sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1503         if(sqy < sqx) sqx = sqy;
1504         if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1505             lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1506             sqx = (wp->width  - lg - marginW) / BOARD_WIDTH - lg;
1507             sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1508             if(sqy < sqx) sqx = sqy;
1509         }
1510         if(sqx != squareSize) {
1511 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1512             squareSize = sqx; // adopt new square size
1513             CreatePNGPieces(); // make newly scaled pieces
1514             InitDrawingSizes(0, 0); // creates grid etc.
1515         } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1516         w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1517         h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1518         if(optList[W_BOARD].max   > w) optList[W_BOARD].max = w;
1519         if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1520 }
1521
1522 static guint delayedDragTag = 0;
1523
1524 void
1525 DragProc ()
1526 {
1527         static int busy;
1528         if(busy) return;
1529
1530         busy = 1;
1531 //      GetActualPlacement(shellWidget, &wpNew);
1532         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1533            wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1534             busy = 0; return; // false alarm
1535         }
1536         ReSize(&wpNew);
1537         if(appData.useStickyWindows) {
1538             if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1539             if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1540             if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1541             if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1542         }
1543         wpMain = wpNew;
1544         DrawPosition(True, NULL);
1545         if(delayedDragTag) g_source_remove(delayedDragTag);
1546         delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1547         busy = 0;
1548 }
1549
1550 void
1551 DelayedDrag ()
1552 {
1553 //printf("old timr = %d\n", delayedDragTag);
1554     if(delayedDragTag) g_source_remove(delayedDragTag);
1555     delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1556 //printf("new timr = %d\n", delayedDragTag);
1557 }
1558
1559 static gboolean
1560 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1561 {
1562 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1563     // immediately
1564     wpNew.x = event->configure.x;
1565     wpNew.y = event->configure.y;
1566     wpNew.width  = event->configure.width;
1567     wpNew.height = event->configure.height;
1568     DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1569     return FALSE;
1570 }
1571
1572
1573
1574 /* Disable all user input other than deleting the window */
1575 static int frozen = 0;
1576
1577 void
1578 FreezeUI ()
1579 {
1580   if (frozen) return;
1581   /* Grab by a widget that doesn't accept input */
1582   gtk_grab_add(optList[W_MESSG].handle);
1583   frozen = 1;
1584 }
1585
1586 /* Undo a FreezeUI */
1587 void
1588 ThawUI ()
1589 {
1590   if (!frozen) return;
1591   gtk_grab_remove(optList[W_MESSG].handle);
1592   frozen = 0;
1593 }
1594
1595 void
1596 ModeHighlight ()
1597 {
1598     static int oldPausing = FALSE;
1599     static GameMode oldmode = (GameMode) -1;
1600     char *wname;
1601     if (!boardWidget) return;
1602
1603     if (pausing != oldPausing) {
1604         oldPausing = pausing;
1605         MarkMenuItem("Mode.Pause", pausing);
1606
1607         if (appData.showButtonBar) {
1608           /* Always toggle, don't set.  Previous code messes up when
1609              invoked while the button is pressed, as releasing it
1610              toggles the state again. */
1611             GdkColor color;
1612             gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1613             gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1614         }
1615     }
1616
1617     wname = ModeToWidgetName(oldmode);
1618     if (wname != NULL) {
1619         MarkMenuItem(wname, False);
1620     }
1621     wname = ModeToWidgetName(gameMode);
1622     if (wname != NULL) {
1623         MarkMenuItem(wname, True);
1624     }
1625     oldmode = gameMode;
1626     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1627
1628     /* Maybe all the enables should be handled here, not just this one */
1629     EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1630
1631     DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1632 }
1633
1634
1635 /*
1636  * Button/menu procedures
1637  */
1638
1639 void CopyFileToClipboard(gchar *filename)
1640 {
1641     gchar *selection_tmp;
1642     GtkClipboard *cb;
1643
1644     // read the file
1645     FILE* f = fopen(filename, "r");
1646     long len;
1647     size_t count;
1648     if (f == NULL) return;
1649     fseek(f, 0, 2);
1650     len = ftell(f);
1651     rewind(f);
1652     selection_tmp = g_try_malloc(len + 1);
1653     if (selection_tmp == NULL) {
1654         printf("Malloc failed in CopyFileToClipboard\n");
1655         return;
1656     }
1657     count = fread(selection_tmp, 1, len, f);
1658     fclose(f);
1659     if (len != count) {
1660       g_free(selection_tmp);
1661       return;
1662     }
1663     selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1664
1665     // copy selection_tmp to clipboard
1666     GdkDisplay *gdisp = gdk_display_get_default();
1667     if (!gdisp) {
1668         g_free(selection_tmp);
1669         return;
1670     }
1671     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1672     gtk_clipboard_set_text(cb, selection_tmp, -1);
1673     g_free(selection_tmp);
1674 }
1675
1676 void
1677 CopySomething (char *src)
1678 {
1679     GdkDisplay *gdisp = gdk_display_get_default();
1680     GtkClipboard *cb;
1681     if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1682     if (gdisp == NULL) return;
1683     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1684     gtk_clipboard_set_text(cb, src, -1);
1685 }
1686
1687 void
1688 PastePositionProc ()
1689 {
1690     GdkDisplay *gdisp = gdk_display_get_default();
1691     GtkClipboard *cb;
1692     gchar *fenstr;
1693
1694     if (gdisp == NULL) return;
1695     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1696     fenstr = gtk_clipboard_wait_for_text(cb);
1697     if (fenstr==NULL) return; // nothing had been selected to copy
1698     EditPositionPasteFEN(fenstr);
1699     return;
1700 }
1701
1702 void
1703 PasteGameProc ()
1704 {
1705     gchar *text=NULL;
1706     GtkClipboard *cb;
1707     guint len=0;
1708     FILE* f;
1709
1710     // get game from clipboard
1711     GdkDisplay *gdisp = gdk_display_get_default();
1712     if (gdisp == NULL) return;
1713     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1714     text = gtk_clipboard_wait_for_text(cb);
1715     if (text == NULL) return; // nothing to paste
1716     len = strlen(text);
1717
1718     // write to temp file
1719     if (text == NULL || len == 0) {
1720       return; //nothing to paste
1721     }
1722     f = fopen(gamePasteFilename, "w");
1723     if (f == NULL) {
1724       DisplayError(_("Can't open temp file"), errno);
1725       return;
1726     }
1727     fwrite(text, 1, len, f);
1728     fclose(f);
1729
1730     // load from file
1731     LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1732     return;
1733 }
1734
1735
1736 #ifdef TODO_GTK
1737 void
1738 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1739 {
1740     QuitProc();
1741 }
1742 #endif
1743
1744 void MoveTypeInProc(eventkey)
1745     GdkEventKey  *eventkey;
1746 {
1747     char buf[10];
1748
1749     // ingnore if ctrl, alt, or meta is pressed
1750     if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1751         return;
1752     }
1753
1754     buf[0]=eventkey->keyval;
1755     buf[1]='\0';
1756     if (eventkey->keyval > 32 && eventkey->keyval < 256)
1757         BoxAutoPopUp (buf);
1758 }
1759
1760 #ifdef TODO_GTK
1761 void
1762 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1763 {
1764         if (!TempBackwardActive) {
1765                 TempBackwardActive = True;
1766                 BackwardEvent();
1767         }
1768 }
1769
1770 void
1771 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1772 {
1773         /* Check to see if triggered by a key release event for a repeating key.
1774          * If so the next queued event will be a key press of the same key at the same time */
1775         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1776                 XEvent next;
1777                 XPeekEvent(xDisplay, &next);
1778                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1779                         next.xkey.keycode == event->xkey.keycode)
1780                                 return;
1781         }
1782     ForwardEvent();
1783         TempBackwardActive = False;
1784 }
1785 #endif
1786
1787 void
1788 ManProc ()
1789 {   // called from menu
1790 #ifdef __APPLE__
1791     system("%s ./man.command", appData.sysOpen);
1792 #else
1793     system("xterm -e man xboard &");
1794 #endif
1795 }
1796
1797 void
1798 SetWindowTitle (char *text, char *title, char *icon)
1799 {
1800 #ifdef TODO_GTK
1801     Arg args[16];
1802     int i;
1803     if (appData.titleInWindow) {
1804         i = 0;
1805         XtSetArg(args[i], XtNlabel, text);   i++;
1806         XtSetValues(titleWidget, args, i);
1807     }
1808     i = 0;
1809     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
1810     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
1811     XtSetValues(shellWidget, args, i);
1812     XSync(xDisplay, False);
1813 #endif
1814     if (appData.titleInWindow) {
1815         SetWidgetLabel(titleWidget, text);
1816     }
1817     gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1818 }
1819
1820
1821 void
1822 DisplayIcsInteractionTitle (String message)
1823 {
1824 #ifdef TODO_GTK
1825   if (oldICSInteractionTitle == NULL) {
1826     /* Magic to find the old window title, adapted from vim */
1827     char *wina = getenv("WINDOWID");
1828     if (wina != NULL) {
1829       Window win = (Window) atoi(wina);
1830       Window root, parent, *children;
1831       unsigned int nchildren;
1832       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1833       for (;;) {
1834         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1835         if (!XQueryTree(xDisplay, win, &root, &parent,
1836                         &children, &nchildren)) break;
1837         if (children) XFree((void *)children);
1838         if (parent == root || parent == 0) break;
1839         win = parent;
1840       }
1841       XSetErrorHandler(oldHandler);
1842     }
1843     if (oldICSInteractionTitle == NULL) {
1844       oldICSInteractionTitle = "xterm";
1845     }
1846   }
1847   printf("\033]0;%s\007", message);
1848   fflush(stdout);
1849 #endif
1850 }
1851
1852
1853 void
1854 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1855 {
1856     GtkWidget *w = (GtkWidget *) opt->handle;
1857     GdkColor col;
1858     char *markup;
1859     char bgcolor[10];
1860     char fgcolor[10];
1861
1862     if (highlight) {
1863         strcpy(bgcolor, "black");
1864         strcpy(fgcolor, "white");
1865     } else {
1866         strcpy(bgcolor, "white");
1867         strcpy(fgcolor, "black");
1868     }
1869     if (timer > 0 &&
1870         appData.lowTimeWarning &&
1871         (timer / 1000) < appData.icsAlarmTime) {
1872         strcpy(fgcolor, appData.lowTimeWarningColor);
1873     }
1874
1875     gdk_color_parse( bgcolor, &col );
1876     gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1877
1878     if (appData.clockMode) {
1879         markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1880                                          bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1881     } else {
1882         markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s  </span>",
1883                                          bgcolor, fgcolor, color);
1884     }
1885     gtk_label_set_markup(GTK_LABEL(w), markup);
1886     g_free(markup);
1887 }
1888
1889 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1890
1891 void
1892 SetClockIcon (int color)
1893 {
1894     GdkPixbuf *pm = *clockIcons[color];
1895     if (mainwindowIcon != pm) {
1896         mainwindowIcon = pm;
1897         gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1898     }
1899 }
1900
1901 #define INPUT_SOURCE_BUF_SIZE 8192
1902
1903 typedef struct {
1904     CPKind kind;
1905     int fd;
1906     int lineByLine;
1907     char *unused;
1908     InputCallback func;
1909     guint sid;
1910     char buf[INPUT_SOURCE_BUF_SIZE];
1911     VOIDSTAR closure;
1912 } InputSource;
1913
1914 gboolean
1915 DoInputCallback(io, cond, data)
1916      GIOChannel  *io;
1917      GIOCondition cond;
1918      gpointer    *data;
1919 {
1920   /* read input from one of the input source (for example a chess program, ICS, etc).
1921    * and call a function that will handle the input
1922    */
1923
1924     int count;
1925     int error;
1926     char *p, *q;
1927
1928     /* All information (callback function, file descriptor, etc) is
1929      * saved in an InputSource structure
1930      */
1931     InputSource *is = (InputSource *) data;
1932
1933     if (is->lineByLine) {
1934         count = read(is->fd, is->unused,
1935                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1936         if (count <= 0) {
1937             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1938             return True;
1939         }
1940         is->unused += count;
1941         p = is->buf;
1942         /* break input into lines and call the callback function on each
1943          * line
1944          */
1945         while (p < is->unused) {
1946             q = memchr(p, '\n', is->unused - p);
1947             if (q == NULL) break;
1948             q++;
1949             (is->func)(is, is->closure, p, q - p, 0);
1950             p = q;
1951         }
1952         /* remember not yet used part of the buffer */
1953         q = is->buf;
1954         while (p < is->unused) {
1955             *q++ = *p++;
1956         }
1957         is->unused = q;
1958     } else {
1959       /* read maximum length of input buffer and send the whole buffer
1960        * to the callback function
1961        */
1962         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1963         if (count == -1)
1964           error = errno;
1965         else
1966           error = 0;
1967         (is->func)(is, is->closure, is->buf, count, error);
1968     }
1969     return True; // Must return true or the watch will be removed
1970 }
1971
1972 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1973      ProcRef pr;
1974      int lineByLine;
1975      InputCallback func;
1976      VOIDSTAR closure;
1977 {
1978     InputSource *is;
1979     GIOChannel *channel;
1980     ChildProc *cp = (ChildProc *) pr;
1981
1982     is = (InputSource *) calloc(1, sizeof(InputSource));
1983     is->lineByLine = lineByLine;
1984     is->func = func;
1985     if (pr == NoProc) {
1986         is->kind = CPReal;
1987         is->fd = fileno(stdin);
1988     } else {
1989         is->kind = cp->kind;
1990         is->fd = cp->fdFrom;
1991     }
1992     if (lineByLine)
1993       is->unused = is->buf;
1994     else
1995       is->unused = NULL;
1996
1997    /* GTK-TODO: will this work on windows?*/
1998
1999     channel = g_io_channel_unix_new(is->fd);
2000     g_io_channel_set_close_on_unref (channel, TRUE);
2001     is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2002
2003     is->closure = closure;
2004     return (InputSourceRef) is;
2005 }
2006
2007
2008 void
2009 RemoveInputSource(isr)
2010      InputSourceRef isr;
2011 {
2012     InputSource *is = (InputSource *) isr;
2013
2014     if (is->sid == 0) return;
2015     g_source_remove(is->sid);
2016     is->sid = 0;
2017     return;
2018 }
2019
2020 #ifndef HAVE_USLEEP
2021
2022 static Boolean frameWaiting;
2023
2024 static RETSIGTYPE
2025 FrameAlarm (int sig)
2026 {
2027   frameWaiting = False;
2028   /* In case System-V style signals.  Needed?? */
2029   signal(SIGALRM, FrameAlarm);
2030 }
2031
2032 void
2033 FrameDelay (int time)
2034 {
2035   struct itimerval delay;
2036
2037   if (time > 0) {
2038     frameWaiting = True;
2039     signal(SIGALRM, FrameAlarm);
2040     delay.it_interval.tv_sec =
2041       delay.it_value.tv_sec = time / 1000;
2042     delay.it_interval.tv_usec =
2043       delay.it_value.tv_usec = (time % 1000) * 1000;
2044     setitimer(ITIMER_REAL, &delay, NULL);
2045     while (frameWaiting) pause();
2046     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2047     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2048     setitimer(ITIMER_REAL, &delay, NULL);
2049   }
2050 }
2051
2052 #else
2053
2054 void
2055 FrameDelay (int time)
2056 {
2057 #ifdef TODO_GTK
2058   XSync(xDisplay, False);
2059 #endif
2060 //  gtk_main_iteration_do(False);
2061
2062   if (time > 0)
2063     usleep(time * 1000);
2064 }
2065
2066 #endif
2067
2068 static void
2069 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2070 {
2071     char buf[MSG_SIZ], *logoName = buf;
2072     if(appData.logo[n][0]) {
2073         logoName = appData.logo[n];
2074     } else if(appData.autoLogo) {
2075         if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2076             sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2077         } else if(appData.directory[n] && appData.directory[n][0]) {
2078             sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2079         }
2080     }
2081     if(logoName[0])
2082         { ASSIGN(cps->programLogo, logoName); }
2083 }
2084
2085 void
2086 UpdateLogos (int displ)
2087 {
2088     if(optList[W_WHITE-1].handle == NULL) return;
2089     LoadLogo(&first, 0, 0);
2090     LoadLogo(&second, 1, appData.icsActive);
2091     if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2092     return;
2093 }
2094
2095 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2096      char *label;
2097      char *def;
2098      char *filter;
2099      FileProc proc;
2100      char *openMode;
2101      Boolean pathFlag;
2102      char **name;
2103      FILE **fp;
2104 {
2105   GtkWidget     *dialog;
2106   GtkFileFilter *gtkfilter;
2107   GtkFileFilter *gtkfilter_all;
2108   char space[]     = " ";
2109   char fileext[10] = "";
2110   char *result     = NULL;
2111   char *cp;
2112
2113   /* make a copy of the filter string, so that strtok can work with it*/
2114   cp = strdup(filter);
2115
2116   /* add filters for file extensions */
2117   gtkfilter     = gtk_file_filter_new();
2118   gtkfilter_all = gtk_file_filter_new();
2119
2120   /* one filter to show everything */
2121   gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2122   gtk_file_filter_set_name   (gtkfilter_all, "All Files");
2123
2124   /* add filter if present */
2125   result = strtok(cp, space);
2126   while( result != NULL  ) {
2127     snprintf(fileext,10,"*%s",result);
2128     result = strtok( NULL, space );
2129     gtk_file_filter_add_pattern(gtkfilter, fileext);
2130   };
2131
2132   /* second filter to only show what's useful */
2133   gtk_file_filter_set_name (gtkfilter,filter);
2134
2135   if (openMode[0] == 'r')
2136     {
2137       dialog = gtk_file_chooser_dialog_new (label,
2138                                             NULL,
2139                                             GTK_FILE_CHOOSER_ACTION_OPEN,
2140                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2141                                             GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2142                                             NULL);
2143     }
2144   else
2145     {
2146       dialog = gtk_file_chooser_dialog_new (label,
2147                                             NULL,
2148                                             GTK_FILE_CHOOSER_ACTION_SAVE,
2149                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2150                                             GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2151                                             NULL);
2152       /* add filename suggestions */
2153       if (strlen(def) > 0 )
2154         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2155
2156       //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2157     }
2158
2159   /* add filters */
2160   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2161   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2162   /* activate filter */
2163   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2164
2165   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2166     {
2167       char *filename;
2168       FILE *f;
2169
2170       filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2171
2172       //see loadgamepopup
2173       f = fopen(filename, openMode);
2174       if (f == NULL)
2175         {
2176           DisplayError(_("Failed to open file"), errno);
2177         }
2178       else
2179         {
2180           /* TODO add indec */
2181             *fp = f;
2182             ASSIGN(*name, filename);
2183             ScheduleDelayedEvent(DelayedLoad, 50);
2184         }
2185       g_free (filename);
2186     };
2187
2188   gtk_widget_destroy (dialog);
2189   ModeHighlight();
2190
2191   free(cp);
2192   return;
2193
2194 }