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);
657 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board again
662 { // dummy, as the GTK code does not make colors in advance
667 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
668 { // determine what fonts to use, and create them
670 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
671 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
672 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
673 appData.font = fontTable[MESSAGE_FONT][squareSize];
674 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
675 appData.coordFont = fontTable[COORD_FONT][squareSize];
676 if(!fontIsSet[CONSOLE_FONT] && fontValid[CONSOLE_FONT][squareSize])
677 appData.icsFont = fontTable[CONSOLE_FONT][squareSize];
678 if(!fontIsSet[EDITTAGS_FONT] && fontValid[EDITTAGS_FONT][squareSize])
679 appData.tagsFont = fontTable[EDITTAGS_FONT][squareSize];
680 if(!fontIsSet[COMMENT_FONT] && fontValid[COMMENT_FONT][squareSize])
681 appData.commentFont = fontTable[COMMENT_FONT][squareSize];
682 if(!fontIsSet[MOVEHISTORY_FONT] && fontValid[MOVEHISTORY_FONT][squareSize])
683 appData.historyFont = fontTable[MOVEHISTORY_FONT][squareSize];
684 if(!fontIsSet[GAMELIST_FONT] && fontValid[GAMELIST_FONT][squareSize])
685 appData.gameListFont = fontTable[GAMELIST_FONT][squareSize];
687 appData.font = InsertPxlSize(appData.font, coordFontPxlSize);
688 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
689 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
690 appData.icsFont = InsertPxlSize(appData.icsFont, coordFontPxlSize);
691 appData.tagsFont = InsertPxlSize(appData.tagsFont, coordFontPxlSize);
692 appData.commentFont = InsertPxlSize(appData.commentFont, coordFontPxlSize);
693 appData.historyFont = InsertPxlSize(appData.historyFont, coordFontPxlSize);
694 appData.gameListFont = InsertPxlSize(appData.gameListFont, coordFontPxlSize);
700 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
701 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
702 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
703 appData.font = fontTable[MESSAGE_FONT][squareSize];
704 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
705 appData.coordFont = fontTable[COORD_FONT][squareSize];
708 appData.font = InsertPxlSize(appData.font, fontPxlSize);
709 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
710 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
711 fontSet = CreateFontSet(appData.font);
712 clockFontSet = CreateFontSet(appData.clockFont);
714 /* For the coordFont, use the 0th font of the fontset. */
715 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
716 XFontStruct **font_struct_list;
717 XFontSetExtents *fontSize;
718 char **font_name_list;
719 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
720 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
721 coordFontStruct = XQueryFont(xDisplay, coordFontID);
722 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
723 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
726 appData.font = FindFont(appData.font, fontPxlSize);
727 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
728 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
729 clockFontID = XLoadFont(xDisplay, appData.clockFont);
730 clockFontStruct = XQueryFont(xDisplay, clockFontID);
731 coordFontID = XLoadFont(xDisplay, appData.coordFont);
732 coordFontStruct = XQueryFont(xDisplay, coordFontID);
733 // textHeight in !NLS mode!
735 countFontID = coordFontID; // [HGM] holdings
736 countFontStruct = coordFontStruct;
738 xdb = XtDatabase(xDisplay);
740 XrmPutLineResource(&xdb, "*international: True");
741 vTo.size = sizeof(XFontSet);
742 vTo.addr = (XtPointer) &fontSet;
743 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
745 XrmPutStringResource(&xdb, "*font", appData.font);
756 case ArgInt: p = " N"; break;
757 case ArgString: p = " STR"; break;
758 case ArgBoolean: p = " TF"; break;
759 case ArgSettingsFilename:
760 case ArgBackupSettingsFile:
761 case ArgFilename: p = " FILE"; break;
762 case ArgX: p = " Nx"; break;
763 case ArgY: p = " Ny"; break;
764 case ArgAttribs: p = " TEXTCOL"; break;
765 case ArgColor: p = " COL"; break;
766 case ArgFont: p = " FONT"; break;
767 case ArgBoardSize: p = " SIZE"; break;
768 case ArgFloat: p = " FLOAT"; break;
773 case ArgCommSettings:
785 ArgDescriptor *q, *p = argDescriptors+5;
786 printf("\nXBoard accepts the following options:\n"
787 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
788 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
789 " SIZE = board-size spec(s)\n"
790 " Within parentheses are short forms, or options to set to true or false.\n"
791 " Persistent options (saved in the settings file) are marked with *)\n\n");
793 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
794 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
795 if(p->save) strcat(buf+len, "*");
796 for(q=p+1; q->argLoc == p->argLoc; q++) {
797 if(q->argName[0] == '-') continue;
798 strcat(buf+len, q == p+1 ? " (" : " ");
799 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
801 if(q != p+1) strcat(buf+len, ")");
803 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
806 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
810 SlaveResize (Option *opt)
812 static int slaveW, slaveH, w, h;
815 gtk_widget_get_allocation(shells[DummyDlg], &a);
816 w = a.width; h = a.height;
817 gtk_widget_get_allocation(opt->handle, &a);
818 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
819 slaveH = h - a.height + 13;
821 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
825 LoadIconFile (gchar *svgFilename)
829 snprintf(buf, MSG_SIZ, "%s/%s" IMG, svgDir, svgFilename);
830 return gdk_pixbuf_new_from_file(buf, NULL);
834 static char clickedFile[MSG_SIZ];
838 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
839 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
840 if(suppress) { // we just started XBoard without arguments
841 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
842 } else { // we are running something presumably useful
844 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
845 system(buf); // start new instance on this file
850 GtkosxApplication *theApp;
854 main (int argc, char **argv)
856 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
857 int boardWidth, w, h; //, boardHeight;
859 int forceMono = False;
861 srandom(time(0)); // [HGM] book: make random truly random
863 setbuf(stdout, NULL);
864 setbuf(stderr, NULL);
867 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
868 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
872 if(argc > 1 && !strcmp(argv[1], "--help" )) {
878 gtk_init (&argc, &argv);
880 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
881 char *path = gtkosx_application_get_bundle_path();
883 char *res_path = gtkosx_application_get_resource_path();
884 snprintf(localeDir, MSG_SIZ, "%s/share/locale", res_path); // redefine locale dir for OSX bundle
886 theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
887 snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
888 snprintf(dataDir, MSG_SIZ, "%s/Contents/Resources/share/xboard", path);
889 snprintf(svgDir, MSG_SIZ, "%s/themes/default", dataDir);
890 suppress = (argc == 1 || argc > 1 && argv[1][00] != '-'); // OSX sends signal even if name was already argv[1]!
891 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
892 // we must call application ready before we can get the signal,
893 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
894 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
895 gtkosx_application_ready(theApp);
896 if(argc == 1) { // called without args: OSX open-file signal might follow
897 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
898 usleep(10000); // wait 10 msec (and hope this is long enough).
899 while(gtk_events_pending())
900 gtk_main_iteration(); // process all events that came in upto now
901 suppress = 0; // future open-file signals should start new instance
902 if(clickedFile[0]) { // we were sent an open-file signal with filename!
903 fakeArgv[0] = argv[0];
904 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
910 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
911 typedef struct {char *name, *value; } Config;
912 static Config configList[] = {
913 { "Datadir", DATADIR },
914 { "Sysconfdir", SYSCONFDIR },
919 for(i=0; configList[i].name; i++) {
920 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
921 if(argc > 2) printf("%s", configList[i].value);
922 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
927 /* set up keyboard accelerators group */
928 GtkAccelerators = gtk_accel_group_new();
930 programName = strrchr(argv[0], '/');
931 if (programName == NULL)
932 programName = argv[0];
937 // if (appData.debugMode) {
938 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
941 bindtextdomain(PACKAGE, LOCALEDIR);
942 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
946 appData.boardSize = "";
947 InitAppData(ConvertToLine(argc, argv));
949 if (p == NULL) p = "/tmp";
950 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
951 gameCopyFilename = (char*) malloc(i);
952 gamePasteFilename = (char*) malloc(i);
953 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
954 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
956 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
957 static char buf[MSG_SIZ];
958 snprintf(buf, MSG_SIZ, appData.sysOpen, DATADIR);
959 ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
960 EscapeExpand(buf, appData.firstInitString);
961 appData.firstInitString = strdup(buf);
962 EscapeExpand(buf, appData.secondInitString);
963 appData.secondInitString = strdup(buf);
964 EscapeExpand(buf, appData.firstComputerString);
965 appData.firstComputerString = strdup(buf);
966 EscapeExpand(buf, appData.secondComputerString);
967 appData.secondComputerString = strdup(buf);
970 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
973 if (chdir(chessDir) != 0) {
974 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
980 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
981 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
982 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
983 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
986 setbuf(debugFP, NULL);
990 if (appData.debugMode) {
991 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
995 /* [HGM,HR] make sure board size is acceptable */
996 if(appData.NrFiles > BOARD_FILES ||
997 appData.NrRanks > BOARD_RANKS )
998 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1001 /* This feature does not work; animation needs a rewrite */
1002 appData.highlightDragging = FALSE;
1006 gameInfo.variant = StringToVariant(appData.variant);
1007 InitPosition(FALSE);
1010 * determine size, based on supplied or remembered -size, or screen size
1012 if (isdigit(appData.boardSize[0])) {
1013 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1014 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1015 &fontPxlSize, &smallLayout, &tinyLayout);
1017 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1018 programName, appData.boardSize);
1022 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // scale height
1024 /* Find some defaults; use the nearest known size */
1025 SizeDefaults *szd, *nearest;
1026 int distance = 99999;
1027 nearest = szd = sizeDefaults;
1028 while (szd->name != NULL) {
1029 if (abs(szd->squareSize - squareSize) < distance) {
1031 distance = abs(szd->squareSize - squareSize);
1032 if (distance == 0) break;
1036 if (i < 2) lineGap = nearest->lineGap;
1037 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1038 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1039 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1040 if (i < 6) smallLayout = nearest->smallLayout;
1041 if (i < 7) tinyLayout = nearest->tinyLayout;
1044 SizeDefaults *szd = sizeDefaults;
1045 if (*appData.boardSize == NULLCHAR) {
1046 // GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow)); // TODO: this does not work, as no mainwindow yet
1047 GdkScreen *screen = gdk_screen_get_default();
1048 guint screenwidth = gdk_screen_get_width(screen);
1049 guint screenheight = gdk_screen_get_height(screen);
1050 while (screenwidth < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1051 screenheight < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1054 if (szd->name == NULL) szd--;
1055 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1057 while (szd->name != NULL &&
1058 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1059 if (szd->name == NULL) {
1060 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1061 programName, appData.boardSize);
1065 squareSize = szd->squareSize;
1066 lineGap = szd->lineGap;
1067 clockFontPxlSize = szd->clockFontPxlSize;
1068 coordFontPxlSize = szd->coordFontPxlSize;
1069 fontPxlSize = szd->fontPxlSize;
1070 smallLayout = szd->smallLayout;
1071 tinyLayout = szd->tinyLayout;
1072 // [HGM] font: use defaults from settings file if available and not overruled
1075 defaultLineGap = lineGap;
1076 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1078 /* [HR] height treated separately (hacked) */
1079 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1080 // boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1083 * Determine what fonts to use.
1085 InitializeFonts((2*clockFontPxlSize+1)/3, coordFontPxlSize, fontPxlSize);
1088 * Detect if there are not enough colors available and adapt.
1091 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1092 appData.monoMode = True;
1096 forceMono = MakeColors();
1099 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1101 appData.monoMode = True;
1104 ParseIcsTextColors();
1110 layoutName = "tinyLayout";
1111 } else if (smallLayout) {
1112 layoutName = "smallLayout";
1114 layoutName = "normalLayout";
1117 if(appData.logoSize) appData.logoSize = boardWidth/4-3;
1118 wpMain.width = -1; // prevent popup sizes window
1119 optList = BoardPopUp(squareSize, lineGap, (void*)
1129 InitDrawingHandle(optList + W_BOARD);
1130 shellWidget = shells[BoardWindow];
1131 currBoard = &optList[W_BOARD];
1132 boardWidget = optList[W_BOARD].handle;
1133 menuBarWidget = optList[W_MENU].handle;
1134 dropMenu = optList[W_DROP].handle;
1135 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1137 formWidget = XtParent(boardWidget);
1138 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1139 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1140 XtGetValues(optList[W_WHITE].handle, args, 2);
1141 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1142 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1143 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1144 XtGetValues(optList[W_PAUSE].handle, args, 2);
1148 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1149 // not need to go into InitDrawingSizes().
1153 // add accelerators to main shell
1154 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1157 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1159 WhiteIcon = LoadIconFile("icon_white");
1160 BlackIcon = LoadIconFile("icon_black");
1161 SetClockIcon(0); // sets white icon
1165 * Create a cursor for the board widget.
1168 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1169 XChangeWindowAttributes(xDisplay, xBoardWindow,
1170 CWCursor, &window_attributes);
1174 * Inhibit shell resizing.
1177 shellArgs[0].value = (XtArgVal) &w;
1178 shellArgs[1].value = (XtArgVal) &h;
1179 XtGetValues(shellWidget, shellArgs, 2);
1180 shellArgs[4].value = shellArgs[2].value = w;
1181 shellArgs[5].value = shellArgs[3].value = h;
1182 // XtSetValues(shellWidget, &shellArgs[2], 4);
1185 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1186 // It wil only become known asynchronously, when we first write a string into it.
1187 // This will then change the clock widget height, which triggers resizing the top-level window
1188 // and a configure event. Only then can we know the total height of the top-level window,
1189 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1190 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1193 gtk_widget_get_allocation(shells[BoardWindow], &a);
1194 w = a.width; h = a.height;
1195 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1196 clockKludge = hc = a.height;
1197 gtk_widget_get_allocation(boardWidget, &a);
1198 // marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1199 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1205 if(appData.logoSize)
1206 { // locate and read user logo
1207 char buf[MSG_SIZ], name[MSG_SIZ];
1208 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1209 if(!FindLogo(name, ".logo", buf))
1210 FindLogo(appData.logoDir, name + 6, buf);
1211 ASSIGN(userLogo, buf);
1214 if (appData.animate || appData.animateDragging)
1217 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1218 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1220 /* [AS] Restore layout */
1221 if( wpMoveHistory.visible ) {
1225 if( wpEvalGraph.visible )
1230 if( wpEngineOutput.visible ) {
1231 EngineOutputPopUp();
1234 if( wpConsole.visible && appData.icsActive ) {
1239 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1244 if (errorExitStatus == -1) {
1245 if (appData.icsActive) {
1246 /* We now wait until we see "login:" from the ICS before
1247 sending the logon script (problems with timestamp otherwise) */
1248 /*ICSInitScript();*/
1249 if (appData.icsInputBox) ICSInputBoxPopUp();
1253 signal(SIGWINCH, TermSizeSigHandler);
1255 signal(SIGINT, IntSigHandler);
1256 signal(SIGTERM, IntSigHandler);
1257 if (*appData.cmailGameName != NULLCHAR) {
1258 signal(SIGUSR1, CmailSigHandler);
1263 // XtSetKeyboardFocus(shellWidget, formWidget);
1265 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1268 /* check for GTK events and process them */
1271 gtk_main_iteration();
1274 if (appData.debugMode) fclose(debugFP); // [DM] debug
1281 while(gtk_events_pending()) gtk_main_iteration();
1285 TermSizeSigHandler (int sig)
1291 IntSigHandler (int sig)
1297 CmailSigHandler (int sig)
1302 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1304 /* Activate call-back function CmailSigHandlerCallBack() */
1305 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1307 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1311 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1314 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1316 /**** end signal code ****/
1319 #define Abs(n) ((n)<0 ? -(n) : (n))
1322 InsertPxlSize (char *pattern, int targetPxlSize)
1325 snprintf(buf, MSG_SIZ, pattern, targetPxlSize); // pattern is something like "Sans Bold %d"
1332 InsertPxlSize (char *pattern, int targetPxlSize)
1334 char *base_fnt_lst, strInt[12], *p, *q;
1335 int alternatives, i, len, strIntLen;
1338 * Replace the "*" (if present) in the pixel-size slot of each
1339 * alternative with the targetPxlSize.
1343 while ((p = strchr(p, ',')) != NULL) {
1347 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1348 strIntLen = strlen(strInt);
1349 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1353 while (alternatives--) {
1354 char *comma = strchr(p, ',');
1355 for (i=0; i<14; i++) {
1356 char *hyphen = strchr(p, '-');
1358 if (comma && hyphen > comma) break;
1359 len = hyphen + 1 - p;
1360 if (i == 7 && *p == '*' && len == 2) {
1362 memcpy(q, strInt, strIntLen);
1372 len = comma + 1 - p;
1379 return base_fnt_lst;
1385 CreateFontSet (char *base_fnt_lst)
1388 char **missing_list;
1392 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1393 &missing_list, &missing_count, &def_string);
1394 if (appData.debugMode) {
1396 XFontStruct **font_struct_list;
1397 char **font_name_list;
1398 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1400 fprintf(debugFP, " got list %s, locale %s\n",
1401 XBaseFontNameListOfFontSet(fntSet),
1402 XLocaleOfFontSet(fntSet));
1403 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1404 for (i = 0; i < count; i++) {
1405 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1408 for (i = 0; i < missing_count; i++) {
1409 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1412 if (fntSet == NULL) {
1413 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1419 #else // not ENABLE_NLS
1421 * Find a font that matches "pattern" that is as close as
1422 * possible to the targetPxlSize. Prefer fonts that are k
1423 * pixels smaller to fonts that are k pixels larger. The
1424 * pattern must be in the X Consortium standard format,
1425 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1426 * The return value should be freed with XtFree when no
1431 FindFont (char *pattern, int targetPxlSize)
1433 char **fonts, *p, *best, *scalable, *scalableTail;
1434 int i, j, nfonts, minerr, err, pxlSize;
1436 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1438 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1439 programName, pattern);
1446 for (i=0; i<nfonts; i++) {
1449 if (*p != '-') continue;
1451 if (*p == NULLCHAR) break;
1452 if (*p++ == '-') j++;
1454 if (j < 7) continue;
1457 scalable = fonts[i];
1460 err = pxlSize - targetPxlSize;
1461 if (Abs(err) < Abs(minerr) ||
1462 (minerr > 0 && err < 0 && -err == minerr)) {
1468 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1469 /* If the error is too big and there is a scalable font,
1470 use the scalable font. */
1471 int headlen = scalableTail - scalable;
1472 p = (char *) XtMalloc(strlen(scalable) + 10);
1473 while (isdigit(*scalableTail)) scalableTail++;
1474 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1476 p = (char *) XtMalloc(strlen(best) + 2);
1477 safeStrCpy(p, best, strlen(best)+1 );
1479 if (appData.debugMode) {
1480 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1481 pattern, targetPxlSize, p);
1483 XFreeFontNames(fonts);
1490 MarkMenuItem (char *menuRef, int state)
1492 MenuItem *item = MenuNameToItem(menuRef);
1494 if(item && item->handle) {
1495 ((GtkCheckMenuItem *) (item->handle))->active = state;
1501 EnableNamedMenuItem (char *menuRef, int state)
1503 MenuItem *item = MenuNameToItem(menuRef);
1505 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1510 EnableButtonBar (int state)
1513 XtSetSensitive(optList[W_BUTTON].handle, state);
1519 SetMenuEnables (Enables *enab)
1521 while (enab->name != NULL) {
1522 EnableNamedMenuItem(enab->name, enab->value);
1527 gboolean KeyPressProc(window, eventkey, data)
1529 GdkEventKey *eventkey;
1533 MoveTypeInProc(eventkey); // pop up for typed in moves
1536 /* check for other key values */
1537 switch(eventkey->keyval) {
1549 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1550 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1552 if(*nprms == 0) return;
1553 item = MenuNameToItem(prms[0]);
1554 if(item) ((MenuProc *) item->proc) ();
1568 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1569 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1570 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1571 dmEnables[i].piece);
1572 XtSetSensitive(entry, p != NULL || !appData.testLegality
1573 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1574 && !appData.icsActive));
1576 while (p && *p++ == dmEnables[i].piece) count++;
1577 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1579 XtSetArg(args[j], XtNlabel, label); j++;
1580 XtSetValues(entry, args, j);
1586 do_flash_delay (unsigned long msec)
1592 FlashDelay (int flash_delay)
1594 if(flash_delay) do_flash_delay(flash_delay);
1598 Fraction (int x, int start, int stop)
1600 double f = ((double) x - start)/(stop - start);
1601 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1605 static WindowPlacement wpNew;
1608 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1610 int touch=0, fudge = 4, f = 3;
1611 GetActualPlacement(sh, wp);
1612 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1613 if(abs(wp->x + wp->width + 2*frameX - f - wpMain.x) < fudge) touch = 2; else // left touch
1614 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1615 if(abs(wp->y + wp->height + frameX + frameY - f - wpMain.y) < fudge) touch = 4; // top touch
1616 //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);
1617 if(!touch ) return; // only windows that touch co-move
1618 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1619 int heightInc = wpNew.height - wpMain.height;
1620 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1621 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1622 wp->y += fracTop * heightInc;
1623 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1625 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1627 wp->height += heightInc;
1628 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1629 int widthInc = wpNew.width - wpMain.width;
1630 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1631 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1632 wp->y += fracLeft * widthInc;
1633 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1635 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1637 wp->width += widthInc;
1639 wp->x += wpNew.x - wpMain.x;
1640 wp->y += wpNew.y - wpMain.y;
1641 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1642 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1644 XtSetArg(args[j], XtNx, wp->x); j++;
1645 XtSetArg(args[j], XtNy, wp->y); j++;
1646 XtSetValues(sh, args, j);
1648 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1649 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1650 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1654 ReSize (WindowPlacement *wp)
1657 int sqx, sqy, w, h, hc, lg = lineGap;
1658 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1659 hc = a.height; // clock height can depend on single / double line clock text!
1660 if(clockKludge && hc != clockKludge) wp->height += hc - clockKludge, clockKludge = 0;
1661 wpMain.height = BOARD_HEIGHT * (squareSize + lineGap) + lineGap + marginH + hc;
1662 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1663 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1664 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1665 if(sqy < sqx) sqx = sqy;
1666 if(sqx < 20) return;
1667 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1669 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1670 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1671 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1672 if(sqy < sqx) sqx = sqy;
1673 lg = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1674 if(sqx == oldSqx + 1 && lg == lineGap + 1) sqx = oldSqx, squareSize = 0; // prevent oscillations, force resize by kludge
1676 if(sqx != squareSize) {
1677 squareSize = sqx; // adopt new square size
1678 CreatePNGPieces(); // make newly scaled pieces
1679 InitDrawingSizes(0, 0); // creates grid etc.
1680 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1681 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1682 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1683 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1684 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1687 static guint delayedDragTag = 0;
1696 // GetActualPlacement(shellWidget, &wpNew);
1697 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1698 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1699 busy = 0; return; // false alarm
1702 if(appData.useStickyWindows) {
1703 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1704 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1705 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1706 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1707 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1710 DrawPosition(True, NULL);
1711 if(delayedDragTag) g_source_remove(delayedDragTag);
1712 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1719 //printf("old timr = %d\n", delayedDragTag);
1720 if(delayedDragTag) g_source_remove(delayedDragTag);
1721 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1722 //printf("new timr = %d\n", delayedDragTag);
1726 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1728 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1730 wpNew.x = event->configure.x;
1731 wpNew.y = event->configure.y;
1732 wpNew.width = event->configure.width;
1733 wpNew.height = event->configure.height;
1734 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1740 /* Disable all user input other than deleting the window */
1741 static int frozen = 0;
1747 /* Grab by a widget that doesn't accept input */
1748 gtk_grab_add(optList[W_MESSG].handle);
1752 /* Undo a FreezeUI */
1756 if (!frozen) return;
1757 gtk_grab_remove(optList[W_MESSG].handle);
1764 static int oldPausing = FALSE;
1765 static GameMode oldmode = (GameMode) -1;
1767 if (!boardWidget) return;
1769 if (pausing != oldPausing) {
1770 oldPausing = pausing;
1771 MarkMenuItem("Mode.Pause", pausing);
1773 if (appData.showButtonBar) {
1774 /* Always toggle, don't set. Previous code messes up when
1775 invoked while the button is pressed, as releasing it
1776 toggles the state again. */
1778 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1779 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1783 wname = ModeToWidgetName(oldmode);
1784 if (wname != NULL) {
1785 MarkMenuItem(wname, False);
1787 wname = ModeToWidgetName(gameMode);
1788 if (wname != NULL) {
1789 MarkMenuItem(wname, True);
1792 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1794 /* Maybe all the enables should be handled here, not just this one */
1795 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1797 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1802 * Button/menu procedures
1805 void CopyFileToClipboard(gchar *filename)
1807 gchar *selection_tmp;
1811 FILE* f = fopen(filename, "r");
1814 if (f == NULL) return;
1818 selection_tmp = g_try_malloc(len + 1);
1819 if (selection_tmp == NULL) {
1820 printf("Malloc failed in CopyFileToClipboard\n");
1823 count = fread(selection_tmp, 1, len, f);
1826 g_free(selection_tmp);
1829 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1831 // copy selection_tmp to clipboard
1832 GdkDisplay *gdisp = gdk_display_get_default();
1834 g_free(selection_tmp);
1837 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1838 gtk_clipboard_set_text(cb, selection_tmp, -1);
1839 g_free(selection_tmp);
1843 CopySomething (char *src)
1845 GdkDisplay *gdisp = gdk_display_get_default();
1847 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1848 if (gdisp == NULL) return;
1849 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1850 gtk_clipboard_set_text(cb, src, -1);
1854 PastePositionProc ()
1856 GdkDisplay *gdisp = gdk_display_get_default();
1860 if (gdisp == NULL) return;
1861 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1862 fenstr = gtk_clipboard_wait_for_text(cb);
1863 if (fenstr==NULL) return; // nothing had been selected to copy
1864 EditPositionPasteFEN(fenstr);
1876 // get game from clipboard
1877 GdkDisplay *gdisp = gdk_display_get_default();
1878 if (gdisp == NULL) return;
1879 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1880 text = gtk_clipboard_wait_for_text(cb);
1881 if (text == NULL) return; // nothing to paste
1884 // write to temp file
1885 if (text == NULL || len == 0) {
1886 return; //nothing to paste
1888 f = fopen(gamePasteFilename, "w");
1890 DisplayError(_("Can't open temp file"), errno);
1893 fwrite(text, 1, len, f);
1897 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1904 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1910 void MoveTypeInProc(eventkey)
1911 GdkEventKey *eventkey;
1915 // ingnore if ctrl, alt, or meta is pressed
1916 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1920 buf[0]=eventkey->keyval;
1922 if (eventkey->keyval > 32 && eventkey->keyval < 256)
1923 ConsoleAutoPopUp (buf);
1928 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1930 if (!TempBackwardActive) {
1931 TempBackwardActive = True;
1937 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1939 /* Check to see if triggered by a key release event for a repeating key.
1940 * If so the next queued event will be a key press of the same key at the same time */
1941 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1943 XPeekEvent(xDisplay, &next);
1944 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1945 next.xkey.keycode == event->xkey.keycode)
1949 TempBackwardActive = False;
1955 { // called from menu
1958 snprintf(buf, MSG_SIZ, "%s ./man.command", appData.sysOpen);
1961 system("xterm -e man xboard &");
1966 SetWindowTitle (char *text, char *title, char *icon)
1971 if (appData.titleInWindow) {
1973 XtSetArg(args[i], XtNlabel, text); i++;
1974 XtSetValues(titleWidget, args, i);
1977 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1978 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1979 XtSetValues(shellWidget, args, i);
1980 XSync(xDisplay, False);
1982 if (appData.titleInWindow) {
1983 SetWidgetLabel(titleWidget, text);
1985 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1990 DisplayIcsInteractionTitle (String message)
1993 if (oldICSInteractionTitle == NULL) {
1994 /* Magic to find the old window title, adapted from vim */
1995 char *wina = getenv("WINDOWID");
1997 Window win = (Window) atoi(wina);
1998 Window root, parent, *children;
1999 unsigned int nchildren;
2000 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2002 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2003 if (!XQueryTree(xDisplay, win, &root, &parent,
2004 &children, &nchildren)) break;
2005 if (children) XFree((void *)children);
2006 if (parent == root || parent == 0) break;
2009 XSetErrorHandler(oldHandler);
2011 if (oldICSInteractionTitle == NULL) {
2012 oldICSInteractionTitle = "xterm";
2015 printf("\033]0;%s\007", message);
2022 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2024 GtkWidget *w = (GtkWidget *) opt->handle;
2031 strcpy(bgcolor, "black");
2032 strcpy(fgcolor, "white");
2034 strcpy(bgcolor, "white");
2035 strcpy(fgcolor, "black");
2038 appData.lowTimeWarning &&
2039 (timer / 1000) < appData.icsAlarmTime) {
2040 strcpy(fgcolor, appData.lowTimeWarningColor);
2043 gdk_color_parse( bgcolor, &col );
2044 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2046 if (appData.clockMode) {
2047 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2048 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2049 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2050 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2052 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2053 bgcolor, fgcolor, color);
2054 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2055 // bgcolor, fgcolor, color);
2057 gtk_label_set_markup(GTK_LABEL(w), markup);
2061 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2064 SetClockIcon (int color)
2066 GdkPixbuf *pm = *clockIcons[color];
2067 if (mainwindowIcon != pm) {
2068 mainwindowIcon = pm;
2070 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2072 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2077 #define INPUT_SOURCE_BUF_SIZE 8192
2086 char buf[INPUT_SOURCE_BUF_SIZE];
2091 DoInputCallback(io, cond, data)
2096 /* read input from one of the input source (for example a chess program, ICS, etc).
2097 * and call a function that will handle the input
2104 /* All information (callback function, file descriptor, etc) is
2105 * saved in an InputSource structure
2107 InputSource *is = (InputSource *) data;
2109 if (is->lineByLine) {
2110 count = read(is->fd, is->unused,
2111 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2113 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2114 RemoveInputSource(is); // cease reading stdin
2115 stdoutClosed = TRUE; // suppress future output
2118 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2121 is->unused += count;
2123 /* break input into lines and call the callback function on each
2126 while (p < is->unused) {
2127 q = memchr(p, '\n', is->unused - p);
2128 if (q == NULL) break;
2130 (is->func)(is, is->closure, p, q - p, 0);
2133 /* remember not yet used part of the buffer */
2135 while (p < is->unused) {
2140 /* read maximum length of input buffer and send the whole buffer
2141 * to the callback function
2143 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2148 (is->func)(is, is->closure, is->buf, count, error);
2150 return True; // Must return true or the watch will be removed
2153 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2160 GIOChannel *channel;
2161 ChildProc *cp = (ChildProc *) pr;
2163 is = (InputSource *) calloc(1, sizeof(InputSource));
2164 is->lineByLine = lineByLine;
2168 is->fd = fileno(stdin);
2170 is->kind = cp->kind;
2171 is->fd = cp->fdFrom;
2174 is->unused = is->buf;
2178 /* GTK-TODO: will this work on windows?*/
2180 channel = g_io_channel_unix_new(is->fd);
2181 g_io_channel_set_close_on_unref (channel, TRUE);
2182 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2184 is->closure = closure;
2185 return (InputSourceRef) is;
2190 RemoveInputSource(isr)
2193 InputSource *is = (InputSource *) isr;
2195 if (is->sid == 0) return;
2196 g_source_remove(is->sid);
2203 static Boolean frameWaiting;
2206 FrameAlarm (int sig)
2208 frameWaiting = False;
2209 /* In case System-V style signals. Needed?? */
2210 signal(SIGALRM, FrameAlarm);
2214 FrameDelay (int time)
2216 struct itimerval delay;
2219 frameWaiting = True;
2220 signal(SIGALRM, FrameAlarm);
2221 delay.it_interval.tv_sec =
2222 delay.it_value.tv_sec = time / 1000;
2223 delay.it_interval.tv_usec =
2224 delay.it_value.tv_usec = (time % 1000) * 1000;
2225 setitimer(ITIMER_REAL, &delay, NULL);
2226 while (frameWaiting) pause();
2227 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2228 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2229 setitimer(ITIMER_REAL, &delay, NULL);
2236 FrameDelay (int time)
2239 XSync(xDisplay, False);
2241 // gtk_main_iteration_do(False);
2244 usleep(time * 1000);
2250 FindLogo (char *place, char *name, char *buf)
2251 { // check if file exists in given place
2253 if(!place) return 0;
2254 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2255 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2263 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2265 char buf[MSG_SIZ], *logoName = buf;
2266 if(appData.logo[n][0]) {
2267 logoName = appData.logo[n];
2268 } else if(appData.autoLogo) {
2269 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2270 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2271 } else { // engine; cascade
2272 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2273 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2274 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2275 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2279 { ASSIGN(cps->programLogo, logoName); }
2283 UpdateLogos (int displ)
2285 if(optList[W_WHITE-1].handle == NULL) return;
2286 LoadLogo(&first, 0, 0);
2287 LoadLogo(&second, 1, appData.icsActive);
2288 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2292 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2303 GtkFileFilter *gtkfilter;
2304 GtkFileFilter *gtkfilter_all;
2306 char fileext[10] = "";
2307 char *result = NULL;
2310 /* make a copy of the filter string, so that strtok can work with it*/
2311 cp = strdup(filter);
2313 /* add filters for file extensions */
2314 gtkfilter = gtk_file_filter_new();
2315 gtkfilter_all = gtk_file_filter_new();
2317 /* one filter to show everything */
2318 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2319 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2321 /* add filter if present */
2322 result = strtok(cp, space);
2323 while( result != NULL ) {
2324 snprintf(fileext,10,"*%s",result);
2325 result = strtok( NULL, space );
2326 gtk_file_filter_add_pattern(gtkfilter, fileext);
2329 /* second filter to only show what's useful */
2330 gtk_file_filter_set_name (gtkfilter,filter);
2332 if (openMode[0] == 'r')
2334 dialog = gtk_file_chooser_dialog_new (label,
2336 GTK_FILE_CHOOSER_ACTION_OPEN,
2337 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2338 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2343 dialog = gtk_file_chooser_dialog_new (label,
2345 GTK_FILE_CHOOSER_ACTION_SAVE,
2346 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2347 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2349 /* add filename suggestions */
2350 if (strlen(def) > 0 )
2351 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2353 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2357 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2358 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2359 /* activate filter */
2360 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2362 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2367 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2370 f = fopen(filename, openMode);
2373 DisplayError(_("Failed to open file"), errno);
2377 /* TODO add indec */
2379 ASSIGN(*name, filename);
2380 ScheduleDelayedEvent(DelayedLoad, 50);
2385 gtk_widget_destroy (dialog);