2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
64 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.h>
69 # if HAVE_SYS_SOCKET_H
70 # include <sys/socket.h>
71 # include <netinet/in.h>
73 # else /* not HAVE_SYS_SOCKET_H */
74 # if HAVE_LAN_SOCKET_H
75 # include <lan/socket.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 */
87 #else /* not STDC_HEADERS */
88 extern char *getenv();
91 # else /* not HAVE_STRING_H */
93 # endif /* not HAVE_STRING_H */
94 #endif /* not STDC_HEADERS */
97 # include <sys/fcntl.h>
98 #else /* not HAVE_SYS_FCNTL_H */
101 # endif /* HAVE_FCNTL_H */
102 #endif /* not HAVE_SYS_FCNTL_H */
104 #if HAVE_SYS_SYSTEMINFO_H
105 # include <sys/systeminfo.h>
106 #endif /* HAVE_SYS_SYSTEMINFO_H */
108 #if TIME_WITH_SYS_TIME
109 # include <sys/time.h>
113 # include <sys/time.h>
124 # include <sys/wait.h>
129 # define NAMLEN(dirent) strlen((dirent)->d_name)
130 # define HAVE_DIR_STRUCT
132 # define dirent direct
133 # define NAMLEN(dirent) (dirent)->d_namlen
135 # include <sys/ndir.h>
136 # define HAVE_DIR_STRUCT
139 # include <sys/dir.h>
140 # define HAVE_DIR_STRUCT
144 # define HAVE_DIR_STRUCT
152 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
155 #include "frontend.h"
157 #include "backendz.h"
165 #include "engineoutput.h"
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).
176 // redefine some defaults
180 # undef SETTINGS_FILE
181 # define ICS_LOGON "Library/Preferences/XboardICS.conf"
182 # define DATADIR dataDir
183 # define LOCALEDIR localeDir
184 # define SETTINGS_FILE masterSettings
185 # define SYNC_MENUBAR gtkosx_application_sync_menubar(theApp)
186 char dataDir[MSG_SIZ]; // for expanding ~~
187 char localeDir[MSG_SIZ];
188 char masterSettings[MSG_SIZ];
192 # define SYNC_MENUBAR
199 #define usleep(t) _sleep2(((t)+500)/1000)
203 # define _(s) gettext (s)
204 # define N_(s) gettext_noop (s)
210 int main P((int argc, char **argv));
211 RETSIGTYPE CmailSigHandler P((int sig));
212 RETSIGTYPE IntSigHandler P((int sig));
213 RETSIGTYPE TermSizeSigHandler P((int sig));
214 char *InsertPxlSize P((char *pattern, int targetPxlSize));
216 XFontSet CreateFontSet P((char *base_fnt_lst));
218 char *FindFont P((char *pattern, int targetPxlSize));
220 void DelayedDrag P((void));
221 void ICSInputBoxPopUp P((void));
222 void MoveTypeInProc P((GdkEventKey *eventkey));
223 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
224 Boolean TempBackwardActive = False;
225 void DisplayMove P((int moveNumber));
226 void update_ics_width P(());
227 int CopyMemoProc P(());
228 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
229 static int FindLogo P((char *place, char *name, char *buf));
233 XFontSet fontSet, clockFontSet;
236 XFontStruct *clockFontStruct;
238 Font coordFontID, countFontID;
239 XFontStruct *coordFontStruct, *countFontStruct;
241 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
242 GtkWidget *mainwindow;
244 Option *optList; // contains all widgets of main window
247 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
250 static GdkPixbuf *mainwindowIcon=NULL;
251 static GdkPixbuf *WhiteIcon=NULL;
252 static GdkPixbuf *BlackIcon=NULL;
254 /* key board accelerators */
255 GtkAccelGroup *GtkAccelerators;
257 typedef unsigned int BoardSize;
259 Boolean chessProgram;
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;
267 Dimension textHeight;
269 char *chessDir, *programName, *programVersion;
270 Boolean alwaysOnTop = False;
271 char *icsTextMenuString;
273 char *firstChessProgramNames;
274 char *secondChessProgramNames;
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;
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. */
291 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
298 DropMenuEnables dmEnables[] = {
307 XtResource clientResources[] = {
308 { "flashCount", "flashCount", XtRInt, sizeof(int),
309 XtOffset(AppDataPtr, flashCount), XtRImmediate,
310 (XtPointer) FLASH_COUNT },
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";
321 char ICSInputTranslations[] =
322 "<Key>Up: UpKeyProc() \n "
323 "<Key>Down: DownKeyProc() \n "
324 "<Key>Return: EnterKeyProc() \n";
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";
331 String xboardResources[] = {
332 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
340 gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
343 //---------------------------------------------------------------------------------------------------------
344 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
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)
355 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
358 // front-end part of option handling
360 // [HGM] This platform-dependent table provides the location for storing the color info
361 extern char *crWhite, * crBlack;
365 &appData.whitePieceColor,
366 &appData.blackPieceColor,
367 &appData.lightSquareColor,
368 &appData.darkSquareColor,
369 &appData.highlightSquareColor,
370 &appData.premoveHighlightColor,
371 &appData.lowTimeWarningColor,
382 // [HGM] font: keep a font for each square size, even non-stndard ones
385 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
386 char *fontTable[NUM_FONTS][MAX_SIZE];
389 ParseFont (char *name, int number)
390 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
392 if(sscanf(name, "size%d:", &size)) {
393 // [HGM] font: font is meant for specific boardSize (likely from settings file);
394 // defer processing it until we know if it matches our board size
395 if(!strstr(name, "-*-") && // ignore X-fonts
396 size >= 0 && size<MAX_SIZE) { // for now, fixed limit
397 fontTable[number][size] = strdup(strchr(name, ':')+1);
398 fontValid[number][size] = True;
403 case 0: // CLOCK_FONT
404 appData.clockFont = strdup(name);
406 case 1: // MESSAGE_FONT
407 appData.font = strdup(name);
409 case 2: // COORD_FONT
410 appData.coordFont = strdup(name);
413 appData.icsFont = strdup(name);
416 appData.tagsFont = strdup(name);
419 appData.commentFont = strdup(name);
421 case MOVEHISTORY_FONT:
422 appData.historyFont = strdup(name);
425 appData.gameListFont = strdup(name);
430 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
435 { // only 2 fonts currently
436 appData.clockFont = strdup(CLOCK_FONT_NAME);
437 appData.coordFont = strdup(COORD_FONT_NAME);
438 appData.font = strdup(DEFAULT_FONT_NAME);
439 appData.icsFont = strdup(CONSOLE_FONT_NAME);
440 appData.tagsFont = strdup(TAGS_FONT_NAME);
441 appData.commentFont = strdup(COMMENT_FONT_NAME);
442 appData.historyFont = strdup(HISTORY_FONT_NAME);
443 appData.gameListFont = strdup(GAMELIST_FONT_NAME);
448 { // no-op, until we identify the code for this already in XBoard and move it here
452 ParseColor (int n, char *name)
453 { // in XBoard, just copy the color-name string
454 if(colorVariable[n] && *name == '#') *(char**)colorVariable[n] = strdup(name);
460 return *(char**)colorVariable[n];
464 ParseTextAttribs (ColorClass cc, char *s)
466 (&appData.colorShout)[cc] = strdup(s);
470 ParseBoardSize (void *addr, char *name)
472 appData.boardSize = strdup(name);
477 { // In XBoard the sound-playing program takes care of obtaining the actual sound
481 SetCommPortDefaults ()
482 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
485 // [HGM] args: these three cases taken out to stay in front-end
487 SaveFontArg (FILE *f, ArgDescriptor *ad)
490 int i, n = (int)(intptr_t)ad->argLoc;
492 case 0: // CLOCK_FONT
493 name = appData.clockFont;
495 case 1: // MESSAGE_FONT
498 case 2: // COORD_FONT
499 name = appData.coordFont;
502 name = appData.icsFont;
505 name = appData.tagsFont;
508 name = appData.commentFont;
510 case MOVEHISTORY_FONT:
511 name = appData.historyFont;
514 name = appData.gameListFont;
519 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
520 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
521 fontTable[n][squareSize] = strdup(name);
522 fontValid[n][squareSize] = True;
525 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
526 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
531 { // nothing to do, as the sounds are at all times represented by their text-string names already
535 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
536 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
537 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
541 SaveColor (FILE *f, ArgDescriptor *ad)
542 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
543 if(colorVariable[(int)(intptr_t)ad->argLoc])
544 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
548 SaveBoardSize (FILE *f, char *name, void *addr)
549 { // wrapper to shield back-end from BoardSize & sizeInfo
550 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
554 ParseCommPortSettings (char *s)
555 { // no such option in XBoard (yet)
561 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
565 gtk_widget_get_allocation(shell, &a);
566 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
570 wp->height = a.height;
571 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
572 frameX = 3; frameY = 3; // remember to decide if windows touch
576 GetPlacement (DialogClass dlg, WindowPlacement *wp)
577 { // wrapper to shield back-end from widget type
578 if(shellUp[dlg]) GetActualPlacement(shells[dlg], wp);
583 { // wrapper to shield use of window handles from back-end (make addressible by number?)
584 // In XBoard this will have to wait until awareness of window parameters is implemented
585 GetActualPlacement(shellWidget, &wpMain);
586 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
587 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
588 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
589 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
590 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
591 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
592 GetPlacement(ChatDlg, &wpConsole); if(appData.icsActive) wpConsole.visible = shellUp[ChatDlg];
596 PrintCommPortSettings (FILE *f, char *name)
597 { // This option does not exist in XBoard
601 EnsureOnScreen (int *x, int *y, int minX, int minY)
608 { // [HGM] args: allows testing if main window is realized from back-end
609 return DialogExists(BoardWindow);
613 PopUpStartupDialog ()
614 { // start menu not implemented in XBoard
618 ConvertToLine (int argc, char **argv)
620 static char line[128*1024], buf[1024];
624 for(i=1; i<argc; i++)
626 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
627 && argv[i][0] != '{' )
628 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
630 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
631 strncat(line, buf, 128*1024 - strlen(line) - 1 );
634 line[strlen(line)-1] = NULLCHAR;
638 //--------------------------------------------------------------------------------------------
643 ResizeBoardWindow (int w, int h, int inhibit)
647 // if(clockKludge) return; // ignore as long as clock does not have final height
648 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
650 gtk_widget_get_allocation(shellWidget, &a);
651 marginW = a.width - bw;
652 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
653 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
654 // w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
655 // h += marginH + a.height + 1;
656 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
658 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board again
663 { // dummy, as the GTK code does not make colors in advance
668 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
669 { // determine what fonts to use, and create them
671 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
672 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
673 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
674 appData.font = fontTable[MESSAGE_FONT][squareSize];
675 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
676 appData.coordFont = fontTable[COORD_FONT][squareSize];
677 if(!fontIsSet[CONSOLE_FONT] && fontValid[CONSOLE_FONT][squareSize])
678 appData.icsFont = fontTable[CONSOLE_FONT][squareSize];
679 if(!fontIsSet[EDITTAGS_FONT] && fontValid[EDITTAGS_FONT][squareSize])
680 appData.tagsFont = fontTable[EDITTAGS_FONT][squareSize];
681 if(!fontIsSet[COMMENT_FONT] && fontValid[COMMENT_FONT][squareSize])
682 appData.commentFont = fontTable[COMMENT_FONT][squareSize];
683 if(!fontIsSet[MOVEHISTORY_FONT] && fontValid[MOVEHISTORY_FONT][squareSize])
684 appData.historyFont = fontTable[MOVEHISTORY_FONT][squareSize];
685 if(!fontIsSet[GAMELIST_FONT] && fontValid[GAMELIST_FONT][squareSize])
686 appData.gameListFont = fontTable[GAMELIST_FONT][squareSize];
688 appData.font = InsertPxlSize(appData.font, coordFontPxlSize);
689 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
690 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
691 appData.icsFont = InsertPxlSize(appData.icsFont, coordFontPxlSize);
692 appData.tagsFont = InsertPxlSize(appData.tagsFont, coordFontPxlSize);
693 appData.commentFont = InsertPxlSize(appData.commentFont, coordFontPxlSize);
694 appData.historyFont = InsertPxlSize(appData.historyFont, coordFontPxlSize);
695 appData.gameListFont = InsertPxlSize(appData.gameListFont, coordFontPxlSize);
701 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
702 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
703 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
704 appData.font = fontTable[MESSAGE_FONT][squareSize];
705 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
706 appData.coordFont = fontTable[COORD_FONT][squareSize];
709 appData.font = InsertPxlSize(appData.font, fontPxlSize);
710 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
711 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
712 fontSet = CreateFontSet(appData.font);
713 clockFontSet = CreateFontSet(appData.clockFont);
715 /* For the coordFont, use the 0th font of the fontset. */
716 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
717 XFontStruct **font_struct_list;
718 XFontSetExtents *fontSize;
719 char **font_name_list;
720 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
721 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
722 coordFontStruct = XQueryFont(xDisplay, coordFontID);
723 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
724 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
727 appData.font = FindFont(appData.font, fontPxlSize);
728 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
729 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
730 clockFontID = XLoadFont(xDisplay, appData.clockFont);
731 clockFontStruct = XQueryFont(xDisplay, clockFontID);
732 coordFontID = XLoadFont(xDisplay, appData.coordFont);
733 coordFontStruct = XQueryFont(xDisplay, coordFontID);
734 // textHeight in !NLS mode!
736 countFontID = coordFontID; // [HGM] holdings
737 countFontStruct = coordFontStruct;
739 xdb = XtDatabase(xDisplay);
741 XrmPutLineResource(&xdb, "*international: True");
742 vTo.size = sizeof(XFontSet);
743 vTo.addr = (XtPointer) &fontSet;
744 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
746 XrmPutStringResource(&xdb, "*font", appData.font);
757 case ArgInt: p = " N"; break;
758 case ArgString: p = " STR"; break;
759 case ArgBoolean: p = " TF"; break;
760 case ArgSettingsFilename:
761 case ArgBackupSettingsFile:
762 case ArgFilename: p = " FILE"; break;
763 case ArgX: p = " Nx"; break;
764 case ArgY: p = " Ny"; break;
765 case ArgAttribs: p = " TEXTCOL"; break;
766 case ArgColor: p = " COL"; break;
767 case ArgFont: p = " FONT"; break;
768 case ArgBoardSize: p = " SIZE"; break;
769 case ArgFloat: p = " FLOAT"; break;
774 case ArgCommSettings:
786 ArgDescriptor *q, *p = argDescriptors+5;
787 printf("\nXBoard accepts the following options:\n"
788 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
789 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
790 " SIZE = board-size spec(s)\n"
791 " Within parentheses are short forms, or options to set to true or false.\n"
792 " Persistent options (saved in the settings file) are marked with *)\n\n");
794 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
795 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
796 if(p->save) strcat(buf+len, "*");
797 for(q=p+1; q->argLoc == p->argLoc; q++) {
798 if(q->argName[0] == '-') continue;
799 strcat(buf+len, q == p+1 ? " (" : " ");
800 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
802 if(q != p+1) strcat(buf+len, ")");
804 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
807 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
811 SlaveResize (Option *opt)
813 static int slaveW, slaveH, w, h;
816 gtk_widget_get_allocation(shells[DummyDlg], &a);
817 w = a.width; h = a.height;
818 gtk_widget_get_allocation(opt->handle, &a);
819 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
820 slaveH = h - a.height + 13;
822 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
826 LoadIconFile (gchar *svgFilename)
830 snprintf(buf, MSG_SIZ, "%s/%s" IMG, svgDir, svgFilename);
831 return gdk_pixbuf_new_from_file(buf, NULL);
835 static char clickedFile[MSG_SIZ];
839 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
840 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
841 if(suppress) { // we just started XBoard without arguments
842 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
843 } else { // we are running something presumably useful
845 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
846 system(buf); // start new instance on this file
851 GtkosxApplication *theApp;
855 main (int argc, char **argv)
857 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
858 int boardWidth, w, h; //, boardHeight;
860 int forceMono = False;
862 srandom(time(0)); // [HGM] book: make random truly random
864 setbuf(stdout, NULL);
865 setbuf(stderr, NULL);
868 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
869 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
873 if(argc > 1 && !strcmp(argv[1], "--help" )) {
879 gtk_init (&argc, &argv);
881 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
882 char *path = gtkosx_application_get_bundle_path();
884 char *res_path = gtkosx_application_get_resource_path();
885 snprintf(localeDir, MSG_SIZ, "%s/share/locale", res_path); // redefine locale dir for OSX bundle
887 theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
888 snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
889 snprintf(dataDir, MSG_SIZ, "%s/Contents/Resources/share/xboard", path);
890 snprintf(svgDir, MSG_SIZ, "%s/themes/default", dataDir);
891 suppress = (argc == 1 || argc > 1 && argv[1][00] != '-'); // OSX sends signal even if name was already argv[1]!
892 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
893 // we must call application ready before we can get the signal,
894 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
895 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
896 gtkosx_application_ready(theApp);
897 if(argc == 1) { // called without args: OSX open-file signal might follow
898 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
899 usleep(10000); // wait 10 msec (and hope this is long enough).
900 while(gtk_events_pending())
901 gtk_main_iteration(); // process all events that came in upto now
902 suppress = 0; // future open-file signals should start new instance
903 if(clickedFile[0]) { // we were sent an open-file signal with filename!
904 fakeArgv[0] = argv[0];
905 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
911 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
912 typedef struct {char *name, *value; } Config;
913 static Config configList[] = {
914 { "Datadir", DATADIR },
915 { "Sysconfdir", SYSCONFDIR },
920 for(i=0; configList[i].name; i++) {
921 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
922 if(argc > 2) printf("%s", configList[i].value);
923 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
928 /* set up keyboard accelerators group */
929 GtkAccelerators = gtk_accel_group_new();
931 programName = strrchr(argv[0], '/');
932 if (programName == NULL)
933 programName = argv[0];
938 // if (appData.debugMode) {
939 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
942 bindtextdomain(PACKAGE, LOCALEDIR);
943 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
947 appData.boardSize = "";
948 InitAppData(ConvertToLine(argc, argv));
950 if (p == NULL) p = "/tmp";
951 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
952 gameCopyFilename = (char*) malloc(i);
953 gamePasteFilename = (char*) malloc(i);
954 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
955 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
957 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
958 static char buf[MSG_SIZ];
959 snprintf(buf, MSG_SIZ, appData.sysOpen, DATADIR);
960 ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
961 EscapeExpand(buf, appData.firstInitString);
962 appData.firstInitString = strdup(buf);
963 EscapeExpand(buf, appData.secondInitString);
964 appData.secondInitString = strdup(buf);
965 EscapeExpand(buf, appData.firstComputerString);
966 appData.firstComputerString = strdup(buf);
967 EscapeExpand(buf, appData.secondComputerString);
968 appData.secondComputerString = strdup(buf);
971 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
974 if (chdir(chessDir) != 0) {
975 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
981 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
982 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
983 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
984 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
987 setbuf(debugFP, NULL);
991 if (appData.debugMode) {
992 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
996 /* [HGM,HR] make sure board size is acceptable */
997 if(appData.NrFiles > BOARD_FILES ||
998 appData.NrRanks > BOARD_RANKS )
999 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1002 /* This feature does not work; animation needs a rewrite */
1003 appData.highlightDragging = FALSE;
1007 gameInfo.variant = StringToVariant(appData.variant);
1008 InitPosition(FALSE);
1011 * determine size, based on supplied or remembered -size, or screen size
1013 if (isdigit(appData.boardSize[0])) {
1014 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1015 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1016 &fontPxlSize, &smallLayout, &tinyLayout);
1018 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1019 programName, appData.boardSize);
1023 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // scale height
1025 /* Find some defaults; use the nearest known size */
1026 SizeDefaults *szd, *nearest;
1027 int distance = 99999;
1028 nearest = szd = sizeDefaults;
1029 while (szd->name != NULL) {
1030 if (abs(szd->squareSize - squareSize) < distance) {
1032 distance = abs(szd->squareSize - squareSize);
1033 if (distance == 0) break;
1037 if (i < 2) lineGap = nearest->lineGap;
1038 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1039 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1040 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1041 if (i < 6) smallLayout = nearest->smallLayout;
1042 if (i < 7) tinyLayout = nearest->tinyLayout;
1045 SizeDefaults *szd = sizeDefaults;
1046 if (*appData.boardSize == NULLCHAR) {
1047 // GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow)); // TODO: this does not work, as no mainwindow yet
1048 GdkScreen *screen = gdk_screen_get_default();
1049 guint screenwidth = gdk_screen_get_width(screen);
1050 guint screenheight = gdk_screen_get_height(screen);
1051 while (screenwidth < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1052 screenheight < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1055 if (szd->name == NULL) szd--;
1056 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1058 while (szd->name != NULL &&
1059 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1060 if (szd->name == NULL) {
1061 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1062 programName, appData.boardSize);
1066 squareSize = szd->squareSize;
1067 lineGap = szd->lineGap;
1068 clockFontPxlSize = szd->clockFontPxlSize;
1069 coordFontPxlSize = szd->coordFontPxlSize;
1070 fontPxlSize = szd->fontPxlSize;
1071 smallLayout = szd->smallLayout;
1072 tinyLayout = szd->tinyLayout;
1073 // [HGM] font: use defaults from settings file if available and not overruled
1076 defaultLineGap = lineGap;
1077 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1079 /* [HR] height treated separately (hacked) */
1080 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1081 // boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1084 * Determine what fonts to use.
1086 InitializeFonts((2*clockFontPxlSize+1)/3, coordFontPxlSize, fontPxlSize);
1089 * Detect if there are not enough colors available and adapt.
1092 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1093 appData.monoMode = True;
1097 forceMono = MakeColors();
1100 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1102 appData.monoMode = True;
1105 ParseIcsTextColors();
1111 layoutName = "tinyLayout";
1112 } else if (smallLayout) {
1113 layoutName = "smallLayout";
1115 layoutName = "normalLayout";
1118 if(appData.logoSize) appData.logoSize = boardWidth/4-3;
1119 wpMain.width = -1; // prevent popup sizes window
1120 optList = BoardPopUp(squareSize, lineGap, (void*)
1130 InitDrawingHandle(optList + W_BOARD);
1131 shellWidget = shells[BoardWindow];
1132 currBoard = &optList[W_BOARD];
1133 boardWidget = optList[W_BOARD].handle;
1134 menuBarWidget = optList[W_MENU].handle;
1135 dropMenu = optList[W_DROP].handle;
1136 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1138 formWidget = XtParent(boardWidget);
1139 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1140 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1141 XtGetValues(optList[W_WHITE].handle, args, 2);
1142 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1143 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1144 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1145 XtGetValues(optList[W_PAUSE].handle, args, 2);
1149 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1150 // not need to go into InitDrawingSizes().
1154 // add accelerators to main shell
1155 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1158 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1160 WhiteIcon = LoadIconFile("icon_white");
1161 BlackIcon = LoadIconFile("icon_black");
1162 SetClockIcon(0); // sets white icon
1166 * Create a cursor for the board widget.
1169 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1170 XChangeWindowAttributes(xDisplay, xBoardWindow,
1171 CWCursor, &window_attributes);
1175 * Inhibit shell resizing.
1178 shellArgs[0].value = (XtArgVal) &w;
1179 shellArgs[1].value = (XtArgVal) &h;
1180 XtGetValues(shellWidget, shellArgs, 2);
1181 shellArgs[4].value = shellArgs[2].value = w;
1182 shellArgs[5].value = shellArgs[3].value = h;
1183 // XtSetValues(shellWidget, &shellArgs[2], 4);
1186 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1187 // It wil only become known asynchronously, when we first write a string into it.
1188 // This will then change the clock widget height, which triggers resizing the top-level window
1189 // and a configure event. Only then can we know the total height of the top-level window,
1190 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1191 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1194 gtk_widget_get_allocation(shells[BoardWindow], &a);
1195 w = a.width; h = a.height;
1196 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1197 clockKludge = hc = a.height;
1198 gtk_widget_get_allocation(boardWidget, &a);
1199 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1200 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1206 if(appData.logoSize)
1207 { // locate and read user logo
1208 char buf[MSG_SIZ], name[MSG_SIZ];
1209 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1210 if(!FindLogo(name, ".logo", buf))
1211 FindLogo(appData.logoDir, name + 6, buf);
1212 ASSIGN(userLogo, buf);
1215 if (appData.animate || appData.animateDragging)
1218 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1219 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1221 /* [AS] Restore layout */
1222 if( wpMoveHistory.visible ) {
1226 if( wpEvalGraph.visible )
1231 if( wpEngineOutput.visible ) {
1232 EngineOutputPopUp();
1235 if( wpConsole.visible && appData.icsActive ) {
1240 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1245 if (errorExitStatus == -1) {
1246 if (appData.icsActive) {
1247 /* We now wait until we see "login:" from the ICS before
1248 sending the logon script (problems with timestamp otherwise) */
1249 /*ICSInitScript();*/
1250 if (appData.icsInputBox) ICSInputBoxPopUp();
1254 signal(SIGWINCH, TermSizeSigHandler);
1256 signal(SIGINT, IntSigHandler);
1257 signal(SIGTERM, IntSigHandler);
1258 if (*appData.cmailGameName != NULLCHAR) {
1259 signal(SIGUSR1, CmailSigHandler);
1264 // XtSetKeyboardFocus(shellWidget, formWidget);
1266 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1269 /* check for GTK events and process them */
1272 gtk_main_iteration();
1275 if (appData.debugMode) fclose(debugFP); // [DM] debug
1282 while(gtk_events_pending()) gtk_main_iteration();
1286 TermSizeSigHandler (int sig)
1292 IntSigHandler (int sig)
1298 CmailSigHandler (int sig)
1303 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1305 /* Activate call-back function CmailSigHandlerCallBack() */
1306 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1308 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1312 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1315 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1317 /**** end signal code ****/
1320 #define Abs(n) ((n)<0 ? -(n) : (n))
1323 InsertPxlSize (char *pattern, int targetPxlSize)
1326 snprintf(buf, MSG_SIZ, pattern, targetPxlSize); // pattern is something like "Sans Bold %d"
1333 InsertPxlSize (char *pattern, int targetPxlSize)
1335 char *base_fnt_lst, strInt[12], *p, *q;
1336 int alternatives, i, len, strIntLen;
1339 * Replace the "*" (if present) in the pixel-size slot of each
1340 * alternative with the targetPxlSize.
1344 while ((p = strchr(p, ',')) != NULL) {
1348 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1349 strIntLen = strlen(strInt);
1350 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1354 while (alternatives--) {
1355 char *comma = strchr(p, ',');
1356 for (i=0; i<14; i++) {
1357 char *hyphen = strchr(p, '-');
1359 if (comma && hyphen > comma) break;
1360 len = hyphen + 1 - p;
1361 if (i == 7 && *p == '*' && len == 2) {
1363 memcpy(q, strInt, strIntLen);
1373 len = comma + 1 - p;
1380 return base_fnt_lst;
1386 CreateFontSet (char *base_fnt_lst)
1389 char **missing_list;
1393 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1394 &missing_list, &missing_count, &def_string);
1395 if (appData.debugMode) {
1397 XFontStruct **font_struct_list;
1398 char **font_name_list;
1399 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1401 fprintf(debugFP, " got list %s, locale %s\n",
1402 XBaseFontNameListOfFontSet(fntSet),
1403 XLocaleOfFontSet(fntSet));
1404 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1405 for (i = 0; i < count; i++) {
1406 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1409 for (i = 0; i < missing_count; i++) {
1410 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1413 if (fntSet == NULL) {
1414 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1420 #else // not ENABLE_NLS
1422 * Find a font that matches "pattern" that is as close as
1423 * possible to the targetPxlSize. Prefer fonts that are k
1424 * pixels smaller to fonts that are k pixels larger. The
1425 * pattern must be in the X Consortium standard format,
1426 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1427 * The return value should be freed with XtFree when no
1432 FindFont (char *pattern, int targetPxlSize)
1434 char **fonts, *p, *best, *scalable, *scalableTail;
1435 int i, j, nfonts, minerr, err, pxlSize;
1437 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1439 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1440 programName, pattern);
1447 for (i=0; i<nfonts; i++) {
1450 if (*p != '-') continue;
1452 if (*p == NULLCHAR) break;
1453 if (*p++ == '-') j++;
1455 if (j < 7) continue;
1458 scalable = fonts[i];
1461 err = pxlSize - targetPxlSize;
1462 if (Abs(err) < Abs(minerr) ||
1463 (minerr > 0 && err < 0 && -err == minerr)) {
1469 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1470 /* If the error is too big and there is a scalable font,
1471 use the scalable font. */
1472 int headlen = scalableTail - scalable;
1473 p = (char *) XtMalloc(strlen(scalable) + 10);
1474 while (isdigit(*scalableTail)) scalableTail++;
1475 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1477 p = (char *) XtMalloc(strlen(best) + 2);
1478 safeStrCpy(p, best, strlen(best)+1 );
1480 if (appData.debugMode) {
1481 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1482 pattern, targetPxlSize, p);
1484 XFreeFontNames(fonts);
1491 MarkMenuItem (char *menuRef, int state)
1493 MenuItem *item = MenuNameToItem(menuRef);
1495 if(item && item->handle) {
1496 ((GtkCheckMenuItem *) (item->handle))->active = state;
1502 EnableNamedMenuItem (char *menuRef, int state)
1504 MenuItem *item = MenuNameToItem(menuRef);
1506 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1511 EnableButtonBar (int state)
1514 XtSetSensitive(optList[W_BUTTON].handle, state);
1520 SetMenuEnables (Enables *enab)
1522 while (enab->name != NULL) {
1523 EnableNamedMenuItem(enab->name, enab->value);
1528 gboolean KeyPressProc(window, eventkey, data)
1530 GdkEventKey *eventkey;
1534 MoveTypeInProc(eventkey); // pop up for typed in moves
1537 /* check for other key values */
1538 switch(eventkey->keyval) {
1550 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1551 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1553 if(*nprms == 0) return;
1554 item = MenuNameToItem(prms[0]);
1555 if(item) ((MenuProc *) item->proc) ();
1569 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1570 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1571 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1572 dmEnables[i].piece);
1573 XtSetSensitive(entry, p != NULL || !appData.testLegality
1574 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1575 && !appData.icsActive));
1577 while (p && *p++ == dmEnables[i].piece) count++;
1578 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1580 XtSetArg(args[j], XtNlabel, label); j++;
1581 XtSetValues(entry, args, j);
1587 do_flash_delay (unsigned long msec)
1593 FlashDelay (int flash_delay)
1595 if(flash_delay) do_flash_delay(flash_delay);
1599 Fraction (int x, int start, int stop)
1601 double f = ((double) x - start)/(stop - start);
1602 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1606 static WindowPlacement wpNew;
1609 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1611 int touch=0, fudge = 4, f = 3;
1612 GetActualPlacement(sh, wp);
1613 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1614 if(abs(wp->x + wp->width + 2*frameX - f - wpMain.x) < fudge) touch = 2; else // left touch
1615 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1616 if(abs(wp->y + wp->height + frameX + frameY - f - wpMain.y) < fudge) touch = 4; // top touch
1617 //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);
1618 if(!touch ) return; // only windows that touch co-move
1619 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1620 int heightInc = wpNew.height - wpMain.height;
1621 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1622 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1623 wp->y += fracTop * heightInc;
1624 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1626 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1628 wp->height += heightInc;
1629 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1630 int widthInc = wpNew.width - wpMain.width;
1631 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1632 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1633 wp->y += fracLeft * widthInc;
1634 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1636 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1638 wp->width += widthInc;
1640 wp->x += wpNew.x - wpMain.x;
1641 wp->y += wpNew.y - wpMain.y;
1642 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1643 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1645 XtSetArg(args[j], XtNx, wp->x); j++;
1646 XtSetArg(args[j], XtNy, wp->y); j++;
1647 XtSetValues(sh, args, j);
1649 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1650 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1651 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1655 ReSize (WindowPlacement *wp)
1658 int sqx, sqy, w, h, lg = lineGap;
1659 static int first = 1;
1660 if(wp->width == wpMain.width && wp->height == wpMain.height && !first) return; // not sized
1661 gtk_widget_get_allocation(optList[W_DROP+1].handle, &a); // table that should contain everything
1662 w = a.width; h = a.height;
1663 gtk_widget_get_allocation(shellWidget, &a);
1664 if(a.width < w || a.height < h) { // outer window smaller than dialog content?
1665 w = a.width - w; h = a.height - h; // subtract matrgins, measured as table minus board dimensions
1666 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1667 w += a.width; h += a.height;
1669 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1670 w = a.width; h = a.height;
1672 sqx = (w - lg) / BOARD_WIDTH - lg;
1673 sqy = (h - lg) / BOARD_HEIGHT - lg;
1674 if(sqy < sqx) sqx = sqy;
1675 if(sqx < 20) return;
1676 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1678 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1679 sqx = (w - lg) / BOARD_WIDTH - lg;
1680 sqy = (h - lg) / BOARD_HEIGHT - lg;
1681 if(sqy < sqx) sqx = sqy;
1682 lg = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1683 if(sqx == oldSqx + 1 && lg == lineGap + 1) sqx = oldSqx, squareSize = 0; // prevent oscillations, force resize by kludge
1685 if(sqx != squareSize && !first) {
1686 squareSize = sqx; // adopt new square size
1687 CreatePNGPieces(); // make newly scaled pieces
1688 InitDrawingSizes(0, 0); // creates grid etc.
1689 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1690 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1691 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1692 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1693 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1694 first = appData.fixedSize;
1697 static guint delayedDragTag = 0;
1706 GetActualPlacement(shellWidget, &wpNew);
1707 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1708 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1709 busy = 0; return; // false alarm
1712 if(appData.useStickyWindows) {
1713 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1714 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1715 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1716 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1717 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1720 DrawPosition(True, NULL);
1721 if(delayedDragTag) g_source_remove(delayedDragTag);
1722 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1729 //printf("old timr = %d\n", delayedDragTag);
1730 if(delayedDragTag) g_source_remove(delayedDragTag);
1731 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1732 //printf("new timr = %d\n", delayedDragTag);
1736 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1738 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1740 wpNew.x = event->configure.x;
1741 wpNew.y = event->configure.y;
1742 wpNew.width = event->configure.width;
1743 wpNew.height = event->configure.height;
1744 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1750 /* Disable all user input other than deleting the window */
1751 static int frozen = 0;
1757 /* Grab by a widget that doesn't accept input */
1758 gtk_grab_add(optList[W_MESSG].handle);
1762 /* Undo a FreezeUI */
1766 if (!frozen) return;
1767 gtk_grab_remove(optList[W_MESSG].handle);
1774 static int oldPausing = FALSE;
1775 static GameMode oldmode = (GameMode) -1;
1777 if (!boardWidget) return;
1779 if (pausing != oldPausing) {
1780 oldPausing = pausing;
1781 MarkMenuItem("Mode.Pause", pausing);
1783 if (appData.showButtonBar) {
1784 /* Always toggle, don't set. Previous code messes up when
1785 invoked while the button is pressed, as releasing it
1786 toggles the state again. */
1788 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1789 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1793 wname = ModeToWidgetName(oldmode);
1794 if (wname != NULL) {
1795 MarkMenuItem(wname, False);
1797 wname = ModeToWidgetName(gameMode);
1798 if (wname != NULL) {
1799 MarkMenuItem(wname, True);
1802 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1804 /* Maybe all the enables should be handled here, not just this one */
1805 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1807 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1812 * Button/menu procedures
1815 void CopyFileToClipboard(gchar *filename)
1817 gchar *selection_tmp;
1821 FILE* f = fopen(filename, "r");
1824 if (f == NULL) return;
1828 selection_tmp = g_try_malloc(len + 1);
1829 if (selection_tmp == NULL) {
1830 printf("Malloc failed in CopyFileToClipboard\n");
1833 count = fread(selection_tmp, 1, len, f);
1836 g_free(selection_tmp);
1839 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1841 // copy selection_tmp to clipboard
1842 GdkDisplay *gdisp = gdk_display_get_default();
1844 g_free(selection_tmp);
1847 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1848 gtk_clipboard_set_text(cb, selection_tmp, -1);
1849 g_free(selection_tmp);
1853 CopySomething (char *src)
1855 GdkDisplay *gdisp = gdk_display_get_default();
1857 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1858 if (gdisp == NULL) return;
1859 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1860 gtk_clipboard_set_text(cb, src, -1);
1864 PastePositionProc ()
1866 GdkDisplay *gdisp = gdk_display_get_default();
1870 if (gdisp == NULL) return;
1871 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1872 fenstr = gtk_clipboard_wait_for_text(cb);
1873 if (fenstr==NULL) return; // nothing had been selected to copy
1874 EditPositionPasteFEN(fenstr);
1886 // get game from clipboard
1887 GdkDisplay *gdisp = gdk_display_get_default();
1888 if (gdisp == NULL) return;
1889 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1890 text = gtk_clipboard_wait_for_text(cb);
1891 if (text == NULL) return; // nothing to paste
1894 // write to temp file
1895 if (text == NULL || len == 0) {
1896 return; //nothing to paste
1898 f = fopen(gamePasteFilename, "w");
1900 DisplayError(_("Can't open temp file"), errno);
1903 fwrite(text, 1, len, f);
1907 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1914 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1920 void MoveTypeInProc(eventkey)
1921 GdkEventKey *eventkey;
1925 // ingnore if ctrl, alt, or meta is pressed
1926 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1930 buf[0]=eventkey->keyval;
1932 if (eventkey->keyval > 32 && eventkey->keyval < 256)
1933 ConsoleAutoPopUp (buf);
1938 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1940 if (!TempBackwardActive) {
1941 TempBackwardActive = True;
1947 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1949 /* Check to see if triggered by a key release event for a repeating key.
1950 * If so the next queued event will be a key press of the same key at the same time */
1951 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1953 XPeekEvent(xDisplay, &next);
1954 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1955 next.xkey.keycode == event->xkey.keycode)
1959 TempBackwardActive = False;
1965 { // called from menu
1968 snprintf(buf, MSG_SIZ, "%s ./man.command", appData.sysOpen);
1971 system("xterm -e man xboard &");
1976 SetWindowTitle (char *text, char *title, char *icon)
1981 if (appData.titleInWindow) {
1983 XtSetArg(args[i], XtNlabel, text); i++;
1984 XtSetValues(titleWidget, args, i);
1987 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1988 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1989 XtSetValues(shellWidget, args, i);
1990 XSync(xDisplay, False);
1992 if (appData.titleInWindow) {
1993 SetWidgetLabel(titleWidget, text);
1995 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2000 DisplayIcsInteractionTitle (String message)
2003 if (oldICSInteractionTitle == NULL) {
2004 /* Magic to find the old window title, adapted from vim */
2005 char *wina = getenv("WINDOWID");
2007 Window win = (Window) atoi(wina);
2008 Window root, parent, *children;
2009 unsigned int nchildren;
2010 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2012 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2013 if (!XQueryTree(xDisplay, win, &root, &parent,
2014 &children, &nchildren)) break;
2015 if (children) XFree((void *)children);
2016 if (parent == root || parent == 0) break;
2019 XSetErrorHandler(oldHandler);
2021 if (oldICSInteractionTitle == NULL) {
2022 oldICSInteractionTitle = "xterm";
2025 printf("\033]0;%s\007", message);
2032 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2034 GtkWidget *w = (GtkWidget *) opt->handle;
2041 strcpy(bgcolor, "black");
2042 strcpy(fgcolor, "white");
2044 strcpy(bgcolor, "white");
2045 strcpy(fgcolor, "black");
2048 appData.lowTimeWarning &&
2049 (timer / 1000) < appData.icsAlarmTime) {
2050 strcpy(fgcolor, appData.lowTimeWarningColor);
2053 gdk_color_parse( bgcolor, &col );
2054 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2056 if (appData.clockMode) {
2057 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2058 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2059 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2060 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2062 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2063 bgcolor, fgcolor, color);
2064 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2065 // bgcolor, fgcolor, color);
2067 gtk_label_set_markup(GTK_LABEL(w), markup);
2071 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2074 SetClockIcon (int color)
2076 GdkPixbuf *pm = *clockIcons[color];
2077 if (mainwindowIcon != pm) {
2078 mainwindowIcon = pm;
2080 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2082 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2087 #define INPUT_SOURCE_BUF_SIZE 8192
2096 char buf[INPUT_SOURCE_BUF_SIZE];
2101 DoInputCallback(io, cond, data)
2106 /* read input from one of the input source (for example a chess program, ICS, etc).
2107 * and call a function that will handle the input
2114 /* All information (callback function, file descriptor, etc) is
2115 * saved in an InputSource structure
2117 InputSource *is = (InputSource *) data;
2119 if (is->lineByLine) {
2120 count = read(is->fd, is->unused,
2121 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2123 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2124 RemoveInputSource(is); // cease reading stdin
2125 stdoutClosed = TRUE; // suppress future output
2128 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2131 is->unused += count;
2133 /* break input into lines and call the callback function on each
2136 while (p < is->unused) {
2137 q = memchr(p, '\n', is->unused - p);
2138 if (q == NULL) break;
2140 (is->func)(is, is->closure, p, q - p, 0);
2143 /* remember not yet used part of the buffer */
2145 while (p < is->unused) {
2150 /* read maximum length of input buffer and send the whole buffer
2151 * to the callback function
2153 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2158 (is->func)(is, is->closure, is->buf, count, error);
2160 return True; // Must return true or the watch will be removed
2163 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2170 GIOChannel *channel;
2171 ChildProc *cp = (ChildProc *) pr;
2173 is = (InputSource *) calloc(1, sizeof(InputSource));
2174 is->lineByLine = lineByLine;
2178 is->fd = fileno(stdin);
2180 is->kind = cp->kind;
2181 is->fd = cp->fdFrom;
2184 is->unused = is->buf;
2188 /* GTK-TODO: will this work on windows?*/
2190 channel = g_io_channel_unix_new(is->fd);
2191 g_io_channel_set_close_on_unref (channel, TRUE);
2192 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2194 is->closure = closure;
2195 return (InputSourceRef) is;
2200 RemoveInputSource(isr)
2203 InputSource *is = (InputSource *) isr;
2205 if (is->sid == 0) return;
2206 g_source_remove(is->sid);
2213 static Boolean frameWaiting;
2216 FrameAlarm (int sig)
2218 frameWaiting = False;
2219 /* In case System-V style signals. Needed?? */
2220 signal(SIGALRM, FrameAlarm);
2224 FrameDelay (int time)
2226 struct itimerval delay;
2229 frameWaiting = True;
2230 signal(SIGALRM, FrameAlarm);
2231 delay.it_interval.tv_sec =
2232 delay.it_value.tv_sec = time / 1000;
2233 delay.it_interval.tv_usec =
2234 delay.it_value.tv_usec = (time % 1000) * 1000;
2235 setitimer(ITIMER_REAL, &delay, NULL);
2236 while (frameWaiting) pause();
2237 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2238 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2239 setitimer(ITIMER_REAL, &delay, NULL);
2246 FrameDelay (int time)
2249 XSync(xDisplay, False);
2251 // gtk_main_iteration_do(False);
2254 usleep(time * 1000);
2260 FindLogo (char *place, char *name, char *buf)
2261 { // check if file exists in given place
2263 if(!place) return 0;
2264 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2265 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2273 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2275 char buf[MSG_SIZ], *logoName = buf;
2276 if(appData.logo[n][0]) {
2277 logoName = appData.logo[n];
2278 } else if(appData.autoLogo) {
2279 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2280 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2281 } else { // engine; cascade
2282 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2283 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2284 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2285 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2289 { ASSIGN(cps->programLogo, logoName); }
2293 UpdateLogos (int displ)
2295 if(optList[W_WHITE-1].handle == NULL) return;
2296 LoadLogo(&first, 0, 0);
2297 LoadLogo(&second, 1, appData.icsActive);
2298 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2302 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2313 GtkFileFilter *gtkfilter;
2314 GtkFileFilter *gtkfilter_all;
2316 char fileext[10] = "";
2317 char *result = NULL;
2320 /* make a copy of the filter string, so that strtok can work with it*/
2321 cp = strdup(filter);
2323 /* add filters for file extensions */
2324 gtkfilter = gtk_file_filter_new();
2325 gtkfilter_all = gtk_file_filter_new();
2327 /* one filter to show everything */
2328 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2329 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2331 /* add filter if present */
2332 result = strtok(cp, space);
2333 while( result != NULL ) {
2334 snprintf(fileext,10,"*%s",result);
2335 result = strtok( NULL, space );
2336 gtk_file_filter_add_pattern(gtkfilter, fileext);
2339 /* second filter to only show what's useful */
2340 gtk_file_filter_set_name (gtkfilter,filter);
2342 if (openMode[0] == 'r')
2344 dialog = gtk_file_chooser_dialog_new (label,
2346 GTK_FILE_CHOOSER_ACTION_OPEN,
2347 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2348 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2353 dialog = gtk_file_chooser_dialog_new (label,
2355 GTK_FILE_CHOOSER_ACTION_SAVE,
2356 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2357 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2359 /* add filename suggestions */
2360 if (strlen(def) > 0 )
2361 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2363 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2367 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2368 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2369 /* activate filter */
2370 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2372 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2377 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2380 f = fopen(filename, openMode);
2383 DisplayError(_("Failed to open file"), errno);
2387 /* TODO add indec */
2389 ASSIGN(*name, filename);
2390 ScheduleDelayedEvent(DelayedLoad, 50);
2395 gtk_widget_destroy (dialog);