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