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