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