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