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, 2015, 2016 Free
9 * Software Foundation, Inc.
11 * The following terms apply to Digital Equipment Corporation's copyright
13 * ------------------------------------------------------------------------
16 * Permission to use, copy, modify, and distribute this software and its
17 * documentation for any purpose and without fee is hereby granted,
18 * provided that the above copyright notice appear in all copies and that
19 * both that copyright notice and this permission notice appear in
20 * supporting documentation, and that the name of Digital not be
21 * used in advertising or publicity pertaining to distribution of the
22 * software without specific, written prior permission.
24 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
26 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
27 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
28 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
29 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
31 * ------------------------------------------------------------------------
33 * The following terms apply to the enhanced version of XBoard
34 * distributed by the Free Software Foundation:
35 * ------------------------------------------------------------------------
37 * GNU XBoard is free software: you can redistribute it and/or modify
38 * it under the terms of the GNU General Public License as published by
39 * the Free Software Foundation, either version 3 of the License, or (at
40 * your option) any later version.
42 * GNU XBoard is distributed in the hope that it will be useful, but
43 * WITHOUT ANY WARRANTY; without even the implied warranty of
44 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
45 * General Public License for more details.
47 * You should have received a copy of the GNU General Public License
48 * along with this program. If not, see http://www.gnu.org/licenses/. *
50 *------------------------------------------------------------------------
51 ** See the file ChangeLog for a revision history. */
61 #include <sys/types.h>
65 #include <cairo/cairo.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
179 # undef SETTINGS_FILE
180 # define ICS_LOGON "Library/Preferences/XboardICS.conf"
181 # define LOCALEDIR localeDir
182 # define SETTINGS_FILE masterSettings
183 # define SYNC_MENUBAR gtkosx_application_sync_menubar(theApp)
184 char localeDir[MSG_SIZ];
185 char masterSettings[MSG_SIZ];
189 # define SYNC_MENUBAR
196 #define usleep(t) _sleep2(((t)+500)/1000)
200 # define _(s) gettext (s)
201 # define N_(s) gettext_noop (s)
207 int main P((int argc, char **argv));
208 RETSIGTYPE CmailSigHandler P((int sig));
209 RETSIGTYPE IntSigHandler P((int sig));
210 RETSIGTYPE TermSizeSigHandler P((int sig));
211 char *InsertPxlSize P((char *pattern, int targetPxlSize));
214 XFontSet CreateFontSet P((char *base_fnt_lst));
216 char *FindFont P((char *pattern, int targetPxlSize));
219 void DelayedDrag P((void));
220 void ICSInputBoxPopUp P((void));
221 void MoveTypeInProc P((GdkEventKey *eventkey));
222 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
223 Boolean TempBackwardActive = False;
224 void DisplayMove P((int moveNumber));
225 void update_ics_width P(());
226 int CopyMemoProc P(());
227 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
228 static int FindLogo P((char *place, char *name, char *buf));
232 XFontSet fontSet, clockFontSet;
235 XFontStruct *clockFontStruct;
237 Font coordFontID, countFontID;
238 XFontStruct *coordFontStruct, *countFontStruct;
240 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
241 GtkWidget *mainwindow;
243 Option *optList; // contains all widgets of main window
246 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
249 static GdkPixbuf *mainwindowIcon=NULL;
250 static GdkPixbuf *WhiteIcon=NULL;
251 static GdkPixbuf *BlackIcon=NULL;
253 /* key board accelerators */
254 GtkAccelGroup *GtkAccelerators;
256 typedef unsigned int BoardSize;
258 Boolean chessProgram;
259 static int initialSquareSize;
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);
447 ChangeFont (char **font, int fnr, int size, char *def, int pix)
449 if(!fontValid[fnr][size]) { ASSIGN(fontTable[fnr][size], def); fontIsSet[fnr] = False; } // use default
450 FREE(*font); *font = InsertPxlSize(fontTable[fnr][size], pix);
455 { // no-op, until we identify the code for this already in XBoard and move it here
459 ParseColor (int n, char *name)
460 { // in XBoard, just copy the color-name string
461 if(colorVariable[n] && *name == '#') *(char**)colorVariable[n] = strdup(name);
467 return *(char**)colorVariable[n];
471 ParseTextAttribs (ColorClass cc, char *s)
473 (&appData.colorShout)[cc] = strdup(s);
477 ParseBoardSize (void *addr, char *name)
479 appData.boardSize = strdup(name);
484 { // In XBoard the sound-playing program takes care of obtaining the actual sound
488 SetCommPortDefaults ()
489 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
492 // [HGM] args: these three cases taken out to stay in front-end
494 SaveFontArg (FILE *f, ArgDescriptor *ad)
497 int i, n = (int)(intptr_t)ad->argLoc;
499 case 0: // CLOCK_FONT
500 name = appData.clockFont;
502 case 1: // MESSAGE_FONT
505 case 2: // COORD_FONT
506 name = appData.coordFont;
509 name = appData.icsFont;
512 name = appData.tagsFont;
515 name = appData.commentFont;
517 case MOVEHISTORY_FONT:
518 name = appData.historyFont;
521 name = appData.gameListFont;
526 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
527 if(sizeDefaults[i].squareSize == initialSquareSize) { // only for standard sizes!
528 fontTable[n][initialSquareSize] = strdup(name);
529 fontValid[n][initialSquareSize] = True;
532 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
533 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
538 { // nothing to do, as the sounds are at all times represented by their text-string names already
542 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
543 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
544 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
548 SaveColor (FILE *f, ArgDescriptor *ad)
549 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
550 if(colorVariable[(int)(intptr_t)ad->argLoc])
551 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
555 SaveBoardSize (FILE *f, char *name, void *addr)
556 { // wrapper to shield back-end from BoardSize & sizeInfo
557 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
561 ParseCommPortSettings (char *s)
562 { // no such option in XBoard (yet)
568 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
572 gtk_widget_get_allocation(shell, &a);
573 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
577 wp->height = a.height;
578 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
579 frameX = 3; frameY = 3; // remember to decide if windows touch
583 GetPlacement (DialogClass dlg, WindowPlacement *wp)
584 { // wrapper to shield back-end from widget type
585 if(shellUp[dlg]) GetActualPlacement(shells[dlg], wp);
590 { // wrapper to shield use of window handles from back-end (make addressible by number?)
591 // In XBoard this will have to wait until awareness of window parameters is implemented
592 GetActualPlacement(shellWidget, &wpMain);
593 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
594 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
595 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
596 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
597 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
598 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
599 GetPlacement(ChatDlg, &wpConsole); if(appData.icsActive) wpConsole.visible = shellUp[ChatDlg];
603 PrintCommPortSettings (FILE *f, char *name)
604 { // This option does not exist in XBoard
608 EnsureOnScreen (int *x, int *y, int minX, int minY)
615 { // [HGM] args: allows testing if main window is realized from back-end
616 return DialogExists(BoardWindow);
620 PopUpStartupDialog ()
621 { // start menu not implemented in XBoard
625 ConvertToLine (int argc, char **argv)
627 static char line[128*1024], buf[1024];
631 for(i=1; i<argc; i++)
633 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
634 && argv[i][0] != '{' )
635 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
637 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
638 strncat(line, buf, 128*1024 - strlen(line) - 1 );
641 line[strlen(line)-1] = NULLCHAR;
645 //--------------------------------------------------------------------------------------------
650 ResizeBoardWindow (int w, int h, int inhibit)
654 // if(clockKludge) return; // ignore as long as clock does not have final height
655 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
657 gtk_widget_get_allocation(shellWidget, &a);
658 marginW = a.width - bw;
659 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
660 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
661 // w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
662 // h += marginH + a.height + 1;
663 gtk_window_resize(GTK_WINDOW(shellWidget), w, 10);
665 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board again
670 { // dummy, as the GTK code does not make colors in advance
675 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
676 { // determine what fonts to use, and create them
678 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
679 appData.clockFont = fontTable[CLOCK_FONT][squareSize], fontIsSet[CLOCK_FONT] = True;
680 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
681 appData.font = fontTable[MESSAGE_FONT][squareSize], fontIsSet[MESSAGE_FONT] = True;
682 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
683 appData.coordFont = fontTable[COORD_FONT][squareSize], fontIsSet[COORD_FONT] = True;
684 if(!fontIsSet[CONSOLE_FONT] && fontValid[CONSOLE_FONT][squareSize])
685 appData.icsFont = fontTable[CONSOLE_FONT][squareSize], fontIsSet[CONSOLE_FONT] = True;
686 if(!fontIsSet[EDITTAGS_FONT] && fontValid[EDITTAGS_FONT][squareSize])
687 appData.tagsFont = fontTable[EDITTAGS_FONT][squareSize], fontIsSet[EDITTAGS_FONT] = True;
688 if(!fontIsSet[COMMENT_FONT] && fontValid[COMMENT_FONT][squareSize])
689 appData.commentFont = fontTable[COMMENT_FONT][squareSize], fontIsSet[COMMENT_FONT] = True;
690 if(!fontIsSet[MOVEHISTORY_FONT] && fontValid[MOVEHISTORY_FONT][squareSize])
691 appData.historyFont = fontTable[MOVEHISTORY_FONT][squareSize], fontIsSet[MOVEHISTORY_FONT] = True;
692 if(!fontIsSet[GAMELIST_FONT] && fontValid[GAMELIST_FONT][squareSize])
693 appData.gameListFont = fontTable[GAMELIST_FONT][squareSize], fontIsSet[GAMELIST_FONT] = True;
695 appData.font = InsertPxlSize(appData.font, coordFontPxlSize);
696 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
697 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
698 appData.icsFont = InsertPxlSize(appData.icsFont, coordFontPxlSize);
699 appData.tagsFont = InsertPxlSize(appData.tagsFont, coordFontPxlSize);
700 appData.commentFont = InsertPxlSize(appData.commentFont, coordFontPxlSize);
701 appData.historyFont = InsertPxlSize(appData.historyFont, coordFontPxlSize);
702 appData.gameListFont = InsertPxlSize(appData.gameListFont, coordFontPxlSize);
708 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
709 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
710 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
711 appData.font = fontTable[MESSAGE_FONT][squareSize];
712 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
713 appData.coordFont = fontTable[COORD_FONT][squareSize];
716 appData.font = InsertPxlSize(appData.font, fontPxlSize);
717 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
718 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
719 fontSet = CreateFontSet(appData.font);
720 clockFontSet = CreateFontSet(appData.clockFont);
722 /* For the coordFont, use the 0th font of the fontset. */
723 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
724 XFontStruct **font_struct_list;
725 XFontSetExtents *fontSize;
726 char **font_name_list;
727 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
728 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
729 coordFontStruct = XQueryFont(xDisplay, coordFontID);
730 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
731 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
734 appData.font = FindFont(appData.font, fontPxlSize);
735 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
736 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
737 clockFontID = XLoadFont(xDisplay, appData.clockFont);
738 clockFontStruct = XQueryFont(xDisplay, clockFontID);
739 coordFontID = XLoadFont(xDisplay, appData.coordFont);
740 coordFontStruct = XQueryFont(xDisplay, coordFontID);
741 // textHeight in !NLS mode!
743 countFontID = coordFontID; // [HGM] holdings
744 countFontStruct = coordFontStruct;
746 xdb = XtDatabase(xDisplay);
748 XrmPutLineResource(&xdb, "*international: True");
749 vTo.size = sizeof(XFontSet);
750 vTo.addr = (XtPointer) &fontSet;
751 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
753 XrmPutStringResource(&xdb, "*font", appData.font);
764 case ArgInt: p = " N"; break;
765 case ArgString: p = " STR"; break;
766 case ArgBoolean: p = " TF"; break;
767 case ArgSettingsFilename:
768 case ArgBackupSettingsFile:
769 case ArgFilename: p = " FILE"; break;
770 case ArgX: p = " Nx"; break;
771 case ArgY: p = " Ny"; break;
772 case ArgAttribs: p = " TEXTCOL"; break;
773 case ArgColor: p = " COL"; break;
774 case ArgFont: p = " FONT"; break;
775 case ArgBoardSize: p = " SIZE"; break;
776 case ArgFloat: p = " FLOAT"; break;
781 case ArgCommSettings:
793 ArgDescriptor *q, *p = argDescriptors+5;
794 printf("\nXBoard accepts the following options:\n"
795 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
796 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
797 " SIZE = board-size spec(s)\n"
798 " Within parentheses are short forms, or options to set to true or false.\n"
799 " Persistent options (saved in the settings file) are marked with *)\n\n");
801 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
802 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
803 if(p->save) strcat(buf+len, "*");
804 for(q=p+1; q->argLoc == p->argLoc; q++) {
805 if(q->argName[0] == '-') continue;
806 strcat(buf+len, q == p+1 ? " (" : " ");
807 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
809 if(q != p+1) strcat(buf+len, ")");
811 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
814 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
818 SlaveResize (Option *opt)
820 static int slaveW, slaveH, w, h;
823 gtk_widget_get_allocation(shells[DummyDlg], &a);
824 w = a.width; h = a.height;
825 gtk_widget_get_allocation(opt->handle, &a);
826 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
827 slaveH = h - a.height + 13;
829 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
833 LoadIconFile (gchar *svgFilename)
837 snprintf(buf, MSG_SIZ, "%s/%s" IMG, svgDir, svgFilename);
838 return gdk_pixbuf_new_from_file(buf, NULL);
842 static char clickedFile[MSG_SIZ];
846 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
847 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
850 if(1000*now.sec + now.ms - 1000*started.sec - started.ms < 1000) { // received during first second
851 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
852 } else { // we are running something presumably useful
854 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
855 system(buf); // start new instance on this file
860 GtkosxApplication *theApp;
864 main (int argc, char **argv)
866 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
867 int boardWidth, w, h; //, boardHeight;
869 int forceMono = False;
871 srandom(time(0)); // [HGM] book: make random truly random
873 setbuf(stdout, NULL);
874 setbuf(stderr, NULL);
877 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
878 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
882 if(argc > 1 && !strcmp(argv[1], "--help" )) {
888 gtk_init (&argc, &argv);
890 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
891 char *path = gtkosx_application_get_bundle_path();
893 char *res_path = gtkosx_application_get_resource_path();
894 snprintf(localeDir, MSG_SIZ, "%s/share/locale", res_path); // redefine locale dir for OSX bundle
896 GetTimeMark(&started); // remember start time
897 theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
898 snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
899 snprintf(dataDir, MSG_SIZ, "%s/Contents/Resources/share/xboard", path);
900 snprintf(manDir, MSG_SIZ, "%s/Contents/Resources/share/man", path);
901 snprintf(svgDir, MSG_SIZ, "%s/themes/default", dataDir);
902 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
903 g_signal_connect(theApp, "NSApplicationWillTerminate", G_CALLBACK(ExitEvent), NULL);
904 // we must call application ready before we can get the signal,
905 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
906 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
907 gtkosx_application_ready(theApp);
908 if(argc == 1) { // called without args: OSX open-file signal might follow
909 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
910 usleep(10000); // wait 10 msec (and hope this is long enough).
911 while(gtk_events_pending())
912 gtk_main_iteration(); // process all events that came in upto now
913 if(clickedFile[0]) { // we were sent an open-file signal with filename!
914 fakeArgv[0] = argv[0];
915 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
921 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
922 typedef struct {char *name, *value; } Config;
923 static Config configList[] = {
924 { "Datadir", dataDir },
925 { "Mandir", manDir },
926 { "Sysconfdir", SYSCONFDIR },
931 for(i=0; configList[i].name; i++) {
932 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
933 if(argc > 2) printf("%s", configList[i].value);
934 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
939 /* set up keyboard accelerators group */
940 GtkAccelerators = gtk_accel_group_new();
942 programName = strrchr(argv[0], '/');
943 if (programName == NULL)
944 programName = argv[0];
949 // if (appData.debugMode) {
950 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
953 bindtextdomain(PACKAGE, LOCALEDIR);
954 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
958 appData.boardSize = "";
959 InitAppData(ConvertToLine(argc, argv));
961 if (p == NULL) p = "/tmp";
962 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
963 gameCopyFilename = (char*) malloc(i);
964 gamePasteFilename = (char*) malloc(i);
965 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
966 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
968 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
969 static char buf[MSG_SIZ];
970 snprintf(buf, MSG_SIZ, appData.sysOpen, dataDir);
971 ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
972 EscapeExpand(buf, appData.firstInitString);
973 appData.firstInitString = strdup(buf);
974 EscapeExpand(buf, appData.secondInitString);
975 appData.secondInitString = strdup(buf);
976 EscapeExpand(buf, appData.firstComputerString);
977 appData.firstComputerString = strdup(buf);
978 EscapeExpand(buf, appData.secondComputerString);
979 appData.secondComputerString = strdup(buf);
982 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
985 if (chdir(chessDir) != 0) {
986 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
992 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
993 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
994 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
995 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
998 setbuf(debugFP, NULL);
1002 if (appData.debugMode) {
1003 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1007 /* [HGM,HR] make sure board size is acceptable */
1008 if(appData.NrFiles > BOARD_FILES ||
1009 appData.NrRanks > BOARD_RANKS )
1010 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1013 /* This feature does not work; animation needs a rewrite */
1014 appData.highlightDragging = FALSE;
1018 gameInfo.variant = StringToVariant(appData.variant);
1019 InitPosition(FALSE);
1022 * determine size, based on supplied or remembered -size, or screen size
1024 if (isdigit(appData.boardSize[0])) {
1025 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1026 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1027 &fontPxlSize, &smallLayout, &tinyLayout);
1029 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1030 programName, appData.boardSize);
1034 /* Find some defaults; use the nearest known size */
1035 SizeDefaults *szd, *nearest;
1036 int distance = 99999;
1037 nearest = szd = sizeDefaults;
1038 while (szd->name != NULL) {
1039 if (abs(szd->squareSize - squareSize) < distance) {
1041 distance = abs(szd->squareSize - squareSize);
1042 if (distance == 0) break;
1046 if (i < 2) lineGap = nearest->lineGap;
1047 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1048 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1049 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1050 if (i < 6) smallLayout = nearest->smallLayout;
1051 if (i < 7) tinyLayout = nearest->tinyLayout;
1054 SizeDefaults *szd = sizeDefaults;
1055 if (*appData.boardSize == NULLCHAR) {
1056 // GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow)); // TODO: this does not work, as no mainwindow yet
1057 GdkScreen *screen = gdk_screen_get_default();
1058 guint screenwidth = gdk_screen_get_width(screen);
1059 guint screenheight = gdk_screen_get_height(screen);
1060 while (screenwidth < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1061 screenheight < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1064 if (szd->name == NULL) szd--;
1065 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1067 while (szd->name != NULL &&
1068 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1069 if (szd->name == NULL) {
1070 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1071 programName, appData.boardSize);
1075 squareSize = szd->squareSize;
1076 lineGap = szd->lineGap;
1077 clockFontPxlSize = szd->clockFontPxlSize;
1078 coordFontPxlSize = szd->coordFontPxlSize;
1079 fontPxlSize = szd->fontPxlSize;
1080 smallLayout = szd->smallLayout;
1081 tinyLayout = szd->tinyLayout;
1082 // [HGM] font: use defaults from settings file if available and not overruled
1084 initialSquareSize = squareSize; // [HGM] remember for saving font info
1085 if(BOARD_WIDTH != 8) {
1086 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // keep width the same
1087 lineGap = (squareSize < 37 ? 1 : squareSize < 59 ? 2 : squareSize < 116 ? 3 : 4);
1090 defaultLineGap = lineGap;
1091 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1093 /* [HR] height treated separately (hacked) */
1094 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1095 // boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1098 * Determine what fonts to use.
1100 InitializeFonts((2*clockFontPxlSize+1)/3, coordFontPxlSize, fontPxlSize);
1103 * Detect if there are not enough colors available and adapt.
1106 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1107 appData.monoMode = True;
1111 forceMono = MakeColors();
1114 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1116 appData.monoMode = True;
1119 ParseIcsTextColors();
1125 layoutName = "tinyLayout";
1126 } else if (smallLayout) {
1127 layoutName = "smallLayout";
1129 layoutName = "normalLayout";
1132 if(appData.logoSize) appData.logoSize = boardWidth/4-3;
1133 wpMain.width = -1; // prevent popup sizes window
1134 optList = BoardPopUp(squareSize, lineGap, (void*)
1144 InitDrawingHandle(optList + W_BOARD);
1145 shellWidget = shells[BoardWindow];
1146 currBoard = &optList[W_BOARD];
1147 boardWidget = optList[W_BOARD].handle;
1148 menuBarWidget = optList[W_MENU].handle;
1149 dropMenu = optList[W_DROP].handle;
1150 titleWidget = optList[optList[W_TITLE].type != Skip ? W_TITLE : W_SMALL].handle;
1152 formWidget = XtParent(boardWidget);
1153 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1154 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1155 XtGetValues(optList[W_WHITE].handle, args, 2);
1156 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1157 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1158 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1159 XtGetValues(optList[W_PAUSE].handle, args, 2);
1163 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1164 // not need to go into InitDrawingSizes().
1168 // add accelerators to main shell
1169 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1172 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1174 WhiteIcon = LoadIconFile("icon_white");
1175 BlackIcon = LoadIconFile("icon_black");
1176 SetClockIcon(0); // sets white icon
1180 * Create a cursor for the board widget.
1183 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1184 XChangeWindowAttributes(xDisplay, xBoardWindow,
1185 CWCursor, &window_attributes);
1189 * Inhibit shell resizing.
1192 shellArgs[0].value = (XtArgVal) &w;
1193 shellArgs[1].value = (XtArgVal) &h;
1194 XtGetValues(shellWidget, shellArgs, 2);
1195 shellArgs[4].value = shellArgs[2].value = w;
1196 shellArgs[5].value = shellArgs[3].value = h;
1197 // XtSetValues(shellWidget, &shellArgs[2], 4);
1200 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1201 // It wil only become known asynchronously, when we first write a string into it.
1202 // This will then change the clock widget height, which triggers resizing the top-level window
1203 // and a configure event. Only then can we know the total height of the top-level window,
1204 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1205 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1208 gtk_widget_get_allocation(shells[BoardWindow], &a);
1209 w = a.width; h = a.height;
1210 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1211 clockKludge = hc = a.height;
1212 gtk_widget_get_allocation(boardWidget, &a);
1213 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1214 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1220 if(appData.logoSize)
1221 { // locate and read user logo
1222 char buf[MSG_SIZ], name[MSG_SIZ];
1223 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1224 if(!FindLogo(name, ".logo", buf))
1225 FindLogo(appData.logoDir, name + 6, buf);
1226 ASSIGN(userLogo, buf);
1229 if (appData.animate || appData.animateDragging)
1232 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1233 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1235 /* [AS] Restore layout */
1236 if( wpMoveHistory.visible ) {
1240 if( wpEvalGraph.visible )
1245 if( wpEngineOutput.visible ) {
1246 EngineOutputPopUp();
1249 if( wpConsole.visible && appData.icsActive ) {
1254 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1259 if (errorExitStatus == -1) {
1260 if (appData.icsActive) {
1261 /* We now wait until we see "login:" from the ICS before
1262 sending the logon script (problems with timestamp otherwise) */
1263 /*ICSInitScript();*/
1264 if (appData.icsInputBox) ICSInputBoxPopUp();
1268 signal(SIGWINCH, TermSizeSigHandler);
1270 signal(SIGINT, IntSigHandler);
1271 signal(SIGTERM, IntSigHandler);
1272 if (*appData.cmailGameName != NULLCHAR) {
1273 signal(SIGUSR1, CmailSigHandler);
1278 // XtSetKeyboardFocus(shellWidget, formWidget);
1280 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1283 /* check for GTK events and process them */
1286 gtk_main_iteration();
1289 if (appData.debugMode) fclose(debugFP); // [DM] debug
1296 while(gtk_events_pending()) gtk_main_iteration();
1300 TermSizeSigHandler (int sig)
1306 IntSigHandler (int sig)
1312 CmailSigHandler (int sig)
1317 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1319 /* Activate call-back function CmailSigHandlerCallBack() */
1320 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1322 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1326 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1329 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1331 /**** end signal code ****/
1334 #define Abs(n) ((n)<0 ? -(n) : (n))
1337 InsertPxlSize (char *pattern, int targetPxlSize)
1340 snprintf(buf, MSG_SIZ, pattern, targetPxlSize); // pattern is something like "Sans Bold %d"
1347 InsertPxlSize (char *pattern, int targetPxlSize)
1349 char *base_fnt_lst, strInt[12], *p, *q;
1350 int alternatives, i, len, strIntLen;
1353 * Replace the "*" (if present) in the pixel-size slot of each
1354 * alternative with the targetPxlSize.
1358 while ((p = strchr(p, ',')) != NULL) {
1362 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1363 strIntLen = strlen(strInt);
1364 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1368 while (alternatives--) {
1369 char *comma = strchr(p, ',');
1370 for (i=0; i<14; i++) {
1371 char *hyphen = strchr(p, '-');
1373 if (comma && hyphen > comma) break;
1374 len = hyphen + 1 - p;
1375 if (i == 7 && *p == '*' && len == 2) {
1377 memcpy(q, strInt, strIntLen);
1387 len = comma + 1 - p;
1394 return base_fnt_lst;
1400 CreateFontSet (char *base_fnt_lst)
1403 char **missing_list;
1407 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1408 &missing_list, &missing_count, &def_string);
1409 if (appData.debugMode) {
1411 XFontStruct **font_struct_list;
1412 char **font_name_list;
1413 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1415 fprintf(debugFP, " got list %s, locale %s\n",
1416 XBaseFontNameListOfFontSet(fntSet),
1417 XLocaleOfFontSet(fntSet));
1418 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1419 for (i = 0; i < count; i++) {
1420 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1423 for (i = 0; i < missing_count; i++) {
1424 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1427 if (fntSet == NULL) {
1428 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1434 #else // not ENABLE_NLS
1436 * Find a font that matches "pattern" that is as close as
1437 * possible to the targetPxlSize. Prefer fonts that are k
1438 * pixels smaller to fonts that are k pixels larger. The
1439 * pattern must be in the X Consortium standard format,
1440 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1441 * The return value should be freed with XtFree when no
1446 FindFont (char *pattern, int targetPxlSize)
1448 char **fonts, *p, *best, *scalable, *scalableTail;
1449 int i, j, nfonts, minerr, err, pxlSize;
1451 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1453 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1454 programName, pattern);
1461 for (i=0; i<nfonts; i++) {
1464 if (*p != '-') continue;
1466 if (*p == NULLCHAR) break;
1467 if (*p++ == '-') j++;
1469 if (j < 7) continue;
1472 scalable = fonts[i];
1475 err = pxlSize - targetPxlSize;
1476 if (Abs(err) < Abs(minerr) ||
1477 (minerr > 0 && err < 0 && -err == minerr)) {
1483 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1484 /* If the error is too big and there is a scalable font,
1485 use the scalable font. */
1486 int headlen = scalableTail - scalable;
1487 p = (char *) XtMalloc(strlen(scalable) + 10);
1488 while (isdigit(*scalableTail)) scalableTail++;
1489 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1491 p = (char *) XtMalloc(strlen(best) + 2);
1492 safeStrCpy(p, best, strlen(best)+1 );
1494 if (appData.debugMode) {
1495 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1496 pattern, targetPxlSize, p);
1498 XFreeFontNames(fonts);
1505 MarkMenuItem (char *menuRef, int state)
1507 MenuItem *item = MenuNameToItem(menuRef);
1509 if(item && item->handle) {
1510 ((GtkCheckMenuItem *) (item->handle))->active = state;
1516 EnableNamedMenuItem (char *menuRef, int state)
1518 MenuItem *item = MenuNameToItem(menuRef);
1520 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1525 EnableButtonBar (int state)
1528 XtSetSensitive(optList[W_BUTTON].handle, state);
1534 SetMenuEnables (Enables *enab)
1536 while (enab->name != NULL) {
1537 EnableNamedMenuItem(enab->name, enab->value);
1542 gboolean KeyPressProc(window, eventkey, data)
1544 GdkEventKey *eventkey;
1548 MoveTypeInProc(eventkey); // pop up for typed in moves
1551 /* check for other key values */
1552 switch(eventkey->keyval) {
1564 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1565 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1567 if(*nprms == 0) return;
1568 item = MenuNameToItem(prms[0]);
1569 if(item) ((MenuProc *) item->proc) ();
1583 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1584 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1585 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1586 dmEnables[i].piece);
1587 XtSetSensitive(entry, p != NULL || !appData.testLegality
1588 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1589 && !appData.icsActive));
1591 while (p && *p++ == dmEnables[i].piece) count++;
1592 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1594 XtSetArg(args[j], XtNlabel, label); j++;
1595 XtSetValues(entry, args, j);
1601 do_flash_delay (unsigned long msec)
1607 FlashDelay (int flash_delay)
1609 if(flash_delay) do_flash_delay(flash_delay);
1613 Fraction (int x, int start, int stop)
1615 double f = ((double) x - start)/(stop - start);
1616 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1620 static WindowPlacement wpNew;
1623 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1625 int touch=0, fudge = 4, f = 3;
1626 GetActualPlacement(sh, wp);
1627 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1628 if(abs(wp->x + wp->width + 2*frameX - f - wpMain.x) < fudge) touch = 2; else // left touch
1629 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1630 if(abs(wp->y + wp->height + frameX + frameY - f - wpMain.y) < fudge) touch = 4; // top touch
1631 //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);
1632 if(!touch ) return; // only windows that touch co-move
1633 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1634 int heightInc = wpNew.height - wpMain.height;
1635 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1636 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1637 wp->y += fracTop * heightInc;
1638 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1640 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1642 wp->height += heightInc;
1643 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1644 int widthInc = wpNew.width - wpMain.width;
1645 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1646 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1647 wp->y += fracLeft * widthInc;
1648 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1650 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1652 wp->width += widthInc;
1654 wp->x += wpNew.x - wpMain.x;
1655 wp->y += wpNew.y - wpMain.y;
1656 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1657 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1659 XtSetArg(args[j], XtNx, wp->x); j++;
1660 XtSetArg(args[j], XtNy, wp->y); j++;
1661 XtSetValues(sh, args, j);
1663 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1664 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1665 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1669 ReSize (WindowPlacement *wp)
1672 int sqx, sqy, i, w, h, lg = lineGap;
1673 static int first = 1;
1674 // DisplayBothClocks();
1675 if(wp->width == wpMain.width && wp->height == wpMain.height && !first) return; // not sized
1676 gtk_widget_get_allocation(optList[W_DROP+1].handle, &a); // table that should contain everything
1677 w = a.width; h = a.height;
1678 gtk_widget_get_allocation(shellWidget, &a);
1679 if(a.width < w || a.height < h) { // outer window smaller than dialog content?
1680 w = a.width - w; h = a.height - h; // subtract matrgins, measured as table minus board dimensions
1681 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1682 w += a.width; h += a.height;
1684 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1685 w = a.width; h = a.height;
1687 sqx = (w - lg) / BOARD_WIDTH - lg;
1688 sqy = (h - lg) / BOARD_HEIGHT - lg;
1689 if(sqy < sqx) sqx = sqy;
1690 if(sqx < 20) return;
1691 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1693 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1694 sqx = (w - lg) / BOARD_WIDTH - lg;
1695 sqy = (h - lg) / BOARD_HEIGHT - lg;
1696 if(sqy < sqx) sqx = sqy;
1697 lg = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1698 if(sqx == oldSqx + 1 && lg == lineGap + 1) sqx = oldSqx, squareSize = 0; // prevent oscillations, force resize by kludge
1700 for(h=0; sizeDefaults[h].name && sizeDefaults[h].squareSize*8 > sqx*BOARD_WIDTH; h++) {}
1701 if(initialSquareSize != sizeDefaults[h].squareSize) { // boardSize changed
1702 initialSquareSize = sizeDefaults[h].squareSize; // used for saving font
1703 ChangeFont(&appData.clockFont, CLOCK_FONT, initialSquareSize, CLOCK_FONT_NAME, 2*(sizeDefaults[h].clockFontPxlSize+1)/3);
1704 ChangeFont(&appData.font, MESSAGE_FONT, initialSquareSize, DEFAULT_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1705 DisplayBothClocks();
1706 ApplyFont(&mainOptions[W_MESSG], NULL);
1707 for(i=1; i<6; i++) ApplyFont(&mainOptions[W_BUTTON+i], NULL);
1709 if(!strchr(appData.boardSize, ',')) {
1710 ASSIGN(appData.boardSize, sizeDefaults[h].name);
1713 if(sizeDefaults[h].tinyLayout != tinyLayout) { // alter clipping of menu names to conform to board width
1714 int clip = (tinyLayout = sizeDefaults[h].tinyLayout) + 1;
1716 for(h=1; mainOptions[h].type == DropDown; h++) {
1717 strncpy(text, _(mainOptions[h].name), MSG_SIZ);
1718 if(clip != 1) text[clip + (text[clip-1] == '_')] = NULLCHAR;
1719 gtk_menu_item_set_label((GtkMenuItem *) mainOptions[h].handle, text);
1723 if(sqx != squareSize && !first) {
1724 squareSize = sqx; // adopt new square size
1725 CreatePNGPieces(); // make newly scaled pieces
1726 InitDrawingSizes(0, 0); // creates grid etc.
1727 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1728 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1729 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1730 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1731 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1732 first = appData.fixedSize;
1733 if(twoBoards && shellUp[DummyDlg]) {
1734 SlavePopUp(); dualOptions[3].max = 0; DoEvents(); // calls SlaveResize, kludge to force assigning new canvas
1735 partnerUp = !partnerUp; flipView = !flipView;
1736 DrawPosition(True, NULL);
1737 partnerUp = !partnerUp; flipView = !flipView;
1741 static guint delayedDragTag = 0;
1747 if(busy) { // prevent recursive calling, but postpone interrupting call rather than lose it
1748 if(!delayedDragTag) delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1752 GetActualPlacement(shellWidget, &wpNew);
1753 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1754 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1755 busy = 0; return; // false alarm
1758 if(appData.useStickyWindows) {
1759 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1760 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1761 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1762 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1763 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1766 DrawPosition(True, NULL);
1767 if(delayedDragTag) g_source_remove(delayedDragTag);
1768 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1775 //printf("old timr = %d\n", delayedDragTag);
1776 if(delayedDragTag) g_source_remove(delayedDragTag);
1777 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1778 //printf("new timr = %d\n", delayedDragTag);
1782 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1784 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1786 wpNew.x = event->configure.x;
1787 wpNew.y = event->configure.y;
1788 wpNew.width = event->configure.width;
1789 wpNew.height = event->configure.height;
1790 // SetWidgetLabel(&mainOptions[W_WHITE], ""); SetWidgetLabel(&mainOptions[W_BLACK], "");
1791 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1797 /* Disable all user input other than deleting the window */
1798 static int frozen = 0;
1804 /* Grab by a widget that doesn't accept input */
1805 gtk_grab_add(optList[W_MESSG].handle);
1809 /* Undo a FreezeUI */
1813 if (!frozen) return;
1814 gtk_grab_remove(optList[W_MESSG].handle);
1821 static int oldPausing = FALSE;
1822 static GameMode oldMode = (GameMode) -1;
1824 if (!boardWidget) return;
1826 if (pausing != oldPausing) {
1827 oldPausing = pausing;
1828 MarkMenuItem("Mode.Pause", pausing);
1830 if (appData.showButtonBar) {
1831 /* Always toggle, don't set. Previous code messes up when
1832 invoked while the button is pressed, as releasing it
1833 toggles the state again. */
1835 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1836 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1840 wname = ModeToWidgetName(oldMode);
1841 if (wname != NULL) {
1842 MarkMenuItem(wname, False);
1844 wname = ModeToWidgetName(gameMode);
1845 if (wname != NULL) {
1846 MarkMenuItem(wname, True);
1848 if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
1849 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1852 /* Maybe all the enables should be handled here, not just this one */
1853 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1855 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1860 * Button/menu procedures
1863 void CopyFileToClipboard(gchar *filename)
1865 gchar *selection_tmp;
1869 FILE* f = fopen(filename, "r");
1872 if (f == NULL) return;
1876 selection_tmp = g_try_malloc(len + 1);
1877 if (selection_tmp == NULL) {
1878 printf("Malloc failed in CopyFileToClipboard\n");
1881 count = fread(selection_tmp, 1, len, f);
1884 g_free(selection_tmp);
1887 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1889 // copy selection_tmp to clipboard
1890 GdkDisplay *gdisp = gdk_display_get_default();
1892 g_free(selection_tmp);
1895 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1896 gtk_clipboard_set_text(cb, selection_tmp, -1);
1897 g_free(selection_tmp);
1901 CopySomething (char *src)
1903 GdkDisplay *gdisp = gdk_display_get_default();
1905 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1906 if (gdisp == NULL) return;
1907 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1908 gtk_clipboard_set_text(cb, src, -1);
1912 PastePositionProc ()
1914 GdkDisplay *gdisp = gdk_display_get_default();
1918 if (gdisp == NULL) return;
1919 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1920 fenstr = gtk_clipboard_wait_for_text(cb);
1921 if (fenstr==NULL) return; // nothing had been selected to copy
1922 EditPositionPasteFEN(fenstr);
1931 guint len=0; int flip = appData.flipView;
1934 // get game from clipboard
1935 GdkDisplay *gdisp = gdk_display_get_default();
1936 if (gdisp == NULL) return;
1937 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1938 text = gtk_clipboard_wait_for_text(cb);
1939 if (text == NULL) return; // nothing to paste
1942 // write to temp file
1943 if (text == NULL || len == 0) {
1944 return; //nothing to paste
1946 f = fopen(gamePasteFilename, "w");
1948 DisplayError(_("Can't open temp file"), errno);
1951 fwrite(text, 1, len, f);
1955 if(!appData.autoFlipView) appData.flipView = flipView;
1956 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1957 appData.flipView = flip;
1964 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1970 void MoveTypeInProc(eventkey)
1971 GdkEventKey *eventkey;
1975 // ingnore if ctrl, alt, or meta is pressed
1976 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1980 buf[0]=eventkey->keyval;
1982 if (eventkey->keyval > 32 && eventkey->keyval < 256 || *buf == 27)
1983 ConsoleAutoPopUp (buf);
1988 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1990 if (!TempBackwardActive) {
1991 TempBackwardActive = True;
1997 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1999 /* Check to see if triggered by a key release event for a repeating key.
2000 * If so the next queued event will be a key press of the same key at the same time */
2001 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2003 XPeekEvent(xDisplay, &next);
2004 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2005 next.xkey.keycode == event->xkey.keycode)
2009 TempBackwardActive = False;
2015 { // called from menu
2018 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"man %s/../man/man6/xboard.6\"' -e 'end tell'", dataDir);
2021 system("xterm -e man xboard &");
2030 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"info -d %s/../info -f xboard.info\"' -e 'end tell'", dataDir);
2032 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
2040 SetWindowTitle (char *text, char *title, char *icon)
2045 if (appData.titleInWindow) {
2047 XtSetArg(args[i], XtNlabel, text); i++;
2048 XtSetValues(titleWidget, args, i);
2051 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2052 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2053 XtSetValues(shellWidget, args, i);
2054 XSync(xDisplay, False);
2056 if (appData.titleInWindow) {
2057 SetWidgetLabel(titleWidget, text);
2059 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2064 DisplayIcsInteractionTitle (String message)
2067 if (oldICSInteractionTitle == NULL) {
2068 /* Magic to find the old window title, adapted from vim */
2069 char *wina = getenv("WINDOWID");
2071 Window win = (Window) atoi(wina);
2072 Window root, parent, *children;
2073 unsigned int nchildren;
2074 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2076 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2077 if (!XQueryTree(xDisplay, win, &root, &parent,
2078 &children, &nchildren)) break;
2079 if (children) XFree((void *)children);
2080 if (parent == root || parent == 0) break;
2083 XSetErrorHandler(oldHandler);
2085 if (oldICSInteractionTitle == NULL) {
2086 oldICSInteractionTitle = "xterm";
2089 printf("\033]0;%s\007", message);
2095 LockBoardSize (int after)
2097 static char *oldClockFont, *oldMessgFont;
2099 if(oldMessgFont && !strcmp(oldMessgFont, appData.font) &&
2100 oldClockFont && !strcmp(oldClockFont, appData.clockFont) && after < 2) return; // only do something when font changed
2101 w = BOARD_WIDTH*(squareSize + lineGap) + lineGap;
2102 h = BOARD_HEIGHT*(squareSize + lineGap) + lineGap;
2104 ASSIGN(oldClockFont, appData.clockFont);
2105 ASSIGN(oldMessgFont, appData.font);
2106 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
2108 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board
2110 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
2115 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2117 static int twoLines = -1;
2118 GtkWidget *w = (GtkWidget *) opt->handle;
2120 char *markup, two = (appData.logoSize != 0);
2125 strcpy(bgcolor, "black");
2126 strcpy(fgcolor, "white");
2128 strcpy(bgcolor, "white");
2129 strcpy(fgcolor, "black");
2132 appData.lowTimeWarning &&
2133 (timer / 1000) < appData.icsAlarmTime) {
2134 strcpy(fgcolor, appData.lowTimeWarningColor);
2137 if(! partnerUp && two != twoLines) LockBoardSize(2); // lock board size if clock height changes
2139 gdk_color_parse( bgcolor, &col );
2140 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2142 if (appData.clockMode) {
2143 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2144 bgcolor, fgcolor, color, two ? "\n" : " ", TimeString(timer));
2145 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2146 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2148 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2149 bgcolor, fgcolor, color);
2150 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2151 // bgcolor, fgcolor, color);
2153 gtk_label_set_markup(GTK_LABEL(w), markup);
2156 if(!partnerUp && two != twoLines) LockBoardSize(3), twoLines = two;
2159 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2162 SetClockIcon (int color)
2164 GdkPixbuf *pm = *clockIcons[color];
2165 if (mainwindowIcon != pm) {
2166 mainwindowIcon = pm;
2168 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2170 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2175 #define INPUT_SOURCE_BUF_SIZE 8192
2184 char buf[INPUT_SOURCE_BUF_SIZE];
2189 DoInputCallback(io, cond, data)
2194 /* read input from one of the input source (for example a chess program, ICS, etc).
2195 * and call a function that will handle the input
2202 /* All information (callback function, file descriptor, etc) is
2203 * saved in an InputSource structure
2205 InputSource *is = (InputSource *) data;
2207 if (is->lineByLine) {
2208 count = read(is->fd, is->unused,
2209 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2211 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2212 RemoveInputSource(is); // cease reading stdin
2213 stdoutClosed = TRUE; // suppress future output
2216 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2219 is->unused += count;
2221 /* break input into lines and call the callback function on each
2224 while (p < is->unused) {
2225 q = memchr(p, '\n', is->unused - p);
2226 if (q == NULL) break;
2228 (is->func)(is, is->closure, p, q - p, 0);
2231 /* remember not yet used part of the buffer */
2233 while (p < is->unused) {
2238 /* read maximum length of input buffer and send the whole buffer
2239 * to the callback function
2241 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2246 (is->func)(is, is->closure, is->buf, count, error);
2248 return True; // Must return true or the watch will be removed
2251 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2258 GIOChannel *channel;
2259 ChildProc *cp = (ChildProc *) pr;
2261 is = (InputSource *) calloc(1, sizeof(InputSource));
2262 is->lineByLine = lineByLine;
2266 is->fd = fileno(stdin);
2268 is->kind = cp->kind;
2269 is->fd = cp->fdFrom;
2272 is->unused = is->buf;
2276 /* GTK-TODO: will this work on windows?*/
2278 channel = g_io_channel_unix_new(is->fd);
2279 g_io_channel_set_close_on_unref (channel, TRUE);
2280 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2282 is->closure = closure;
2283 return (InputSourceRef) is;
2288 RemoveInputSource(isr)
2291 InputSource *is = (InputSource *) isr;
2293 if (is->sid == 0) return;
2294 g_source_remove(is->sid);
2301 static Boolean frameWaiting;
2304 FrameAlarm (int sig)
2306 frameWaiting = False;
2307 /* In case System-V style signals. Needed?? */
2308 signal(SIGALRM, FrameAlarm);
2312 FrameDelay (int time)
2314 struct itimerval delay;
2317 frameWaiting = True;
2318 signal(SIGALRM, FrameAlarm);
2319 delay.it_interval.tv_sec =
2320 delay.it_value.tv_sec = time / 1000;
2321 delay.it_interval.tv_usec =
2322 delay.it_value.tv_usec = (time % 1000) * 1000;
2323 setitimer(ITIMER_REAL, &delay, NULL);
2324 while (frameWaiting) pause();
2325 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2326 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2327 setitimer(ITIMER_REAL, &delay, NULL);
2334 FrameDelay (int time)
2337 XSync(xDisplay, False);
2339 // gtk_main_iteration_do(False);
2342 usleep(time * 1000);
2348 FindLogo (char *place, char *name, char *buf)
2349 { // check if file exists in given place
2351 if(!place) return 0;
2352 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2353 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2361 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2363 char buf[MSG_SIZ], *logoName = buf;
2364 if(appData.logo[n][0]) {
2365 logoName = appData.logo[n];
2366 } else if(appData.autoLogo) {
2367 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2368 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2369 } else { // engine; cascade
2370 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2371 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2372 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2373 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2377 { ASSIGN(cps->programLogo, logoName); }
2381 UpdateLogos (int displ)
2383 if(optList[W_WHITE-1].handle == NULL) return;
2384 LoadLogo(&first, 0, 0);
2385 LoadLogo(&second, 1, appData.icsActive);
2386 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2390 void FileNamePopUpWrapper(
2401 GtkFileFilter *gtkfilter;
2402 GtkFileFilter *gtkfilter_all;
2404 char fileext[10] = "";
2405 char *result = NULL;
2407 char curDir[MSG_SIZ];
2409 StartDir(filter, NULL); // change to start directory for this file type
2411 if(def && *def && def[strlen(def)-1] == '/') {
2412 getcwd(curDir, MSG_SIZ);
2417 /* make a copy of the filter string, so that strtok can work with it*/
2418 cp = strdup(filter);
2420 /* add filters for file extensions */
2421 gtkfilter = gtk_file_filter_new();
2422 gtkfilter_all = gtk_file_filter_new();
2424 /* one filter to show everything */
2425 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2426 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2428 /* add filter if present */
2429 result = strtok(cp, space);
2430 while( result != NULL ) {
2431 snprintf(fileext,10,"*%s",result);
2432 result = strtok( NULL, space );
2433 gtk_file_filter_add_pattern(gtkfilter, fileext);
2436 /* second filter to only show what's useful */
2437 gtk_file_filter_set_name (gtkfilter,filter);
2439 if (openMode[0] == 'r')
2441 dialog = gtk_file_chooser_dialog_new (label,
2443 GTK_FILE_CHOOSER_ACTION_OPEN,
2444 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2445 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2450 dialog = gtk_file_chooser_dialog_new (label,
2452 GTK_FILE_CHOOSER_ACTION_SAVE,
2453 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2454 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2456 /* add filename suggestions */
2457 if (strlen(def) > 0 )
2458 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2460 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2464 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2465 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2466 /* activate filter */
2467 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2469 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2474 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2477 f = fopen(filename, openMode);
2480 DisplayError(_("Failed to open file"), errno);
2484 /* TODO add indec */
2486 ASSIGN(*name, filename);
2487 ScheduleDelayedEvent(DelayedLoad, 50);
2489 StartDir(filter, filename);
2492 else StartDir(filter, "");
2494 gtk_widget_destroy (dialog);
2497 if(def && *def && def[strlen(def)-1] == '/') chdir(curDir);