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 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
384 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
385 char *fontTable[NUM_FONTS][MAX_SIZE];
388 ParseFont (char *name, int number)
389 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
391 if(sscanf(name, "size%d:", &size)) {
392 // [HGM] font: font is meant for specific boardSize (likely from settings file);
393 // defer processing it until we know if it matches our board size
394 if(!strstr(name, "-*-") && // ignore X-fonts
395 size >= 0 && size<MAX_SIZE) { // for now, fixed limit
396 fontTable[number][size] = strdup(strchr(name, ':')+1);
397 fontValid[number][size] = True;
402 case 0: // CLOCK_FONT
403 appData.clockFont = strdup(name);
405 case 1: // MESSAGE_FONT
406 appData.font = strdup(name);
408 case 2: // COORD_FONT
409 appData.coordFont = strdup(name);
412 appData.icsFont = strdup(name);
415 appData.tagsFont = strdup(name);
418 appData.commentFont = strdup(name);
420 case MOVEHISTORY_FONT:
421 appData.historyFont = strdup(name);
424 appData.gameListFont = strdup(name);
429 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
434 { // only 2 fonts currently
435 appData.clockFont = strdup(CLOCK_FONT_NAME);
436 appData.coordFont = strdup(COORD_FONT_NAME);
437 appData.font = strdup(DEFAULT_FONT_NAME);
438 appData.icsFont = strdup(CONSOLE_FONT_NAME);
439 appData.tagsFont = strdup(TAGS_FONT_NAME);
440 appData.commentFont = strdup(COMMENT_FONT_NAME);
441 appData.historyFont = strdup(HISTORY_FONT_NAME);
442 appData.gameListFont = strdup(GAMELIST_FONT_NAME);
446 ChangeFont (int force, char **font, int fnr, int size, char *def, int pix)
448 if(!fontValid[fnr][size]) {
449 if(fontIsSet[fnr] && !force) return; // unless forced we do not replace an explicitly specified font by a default
450 ASSIGN(fontTable[fnr][size], def); // use default
451 fontIsSet[fnr] = False;
452 } else fontIsSet[fnr] = True;
453 FREE(*font); *font = InsertPxlSize(fontTable[fnr][size], pix);
458 { // no-op, until we identify the code for this already in XBoard and move it here
462 ParseColor (int n, char *name)
463 { // in XBoard, just copy the color-name string
464 if(colorVariable[n] && *name == '#') *(char**)colorVariable[n] = strdup(name);
470 return *(char**)colorVariable[n];
474 ParseTextAttribs (ColorClass cc, char *s)
476 (&appData.colorShout)[cc] = strdup(s);
480 ParseBoardSize (void *addr, char *name)
482 appData.boardSize = strdup(name);
487 { // In XBoard the sound-playing program takes care of obtaining the actual sound
491 SetCommPortDefaults ()
492 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
495 // [HGM] args: these three cases taken out to stay in front-end
497 SaveFontArg (FILE *f, ArgDescriptor *ad)
500 int i, n = (int)(intptr_t)ad->argLoc;
502 case 0: // CLOCK_FONT
503 name = appData.clockFont;
505 case 1: // MESSAGE_FONT
508 case 2: // COORD_FONT
509 name = appData.coordFont;
512 name = appData.icsFont;
515 name = appData.tagsFont;
518 name = appData.commentFont;
520 case MOVEHISTORY_FONT:
521 name = appData.historyFont;
524 name = appData.gameListFont;
529 if(fontIsSet[n]) // only save fonts that were not defaults
530 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
531 if(sizeDefaults[i].squareSize == initialSquareSize) { // only for standard sizes!
532 ASSIGN(fontTable[n][initialSquareSize], name);
533 fontValid[n][initialSquareSize] = True;
536 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
537 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
542 { // nothing to do, as the sounds are at all times represented by their text-string names already
546 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
547 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
548 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
552 SaveColor (FILE *f, ArgDescriptor *ad)
553 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
554 if(colorVariable[(int)(intptr_t)ad->argLoc])
555 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
559 SaveBoardSize (FILE *f, char *name, void *addr)
560 { // wrapper to shield back-end from BoardSize & sizeInfo
561 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
565 ParseCommPortSettings (char *s)
566 { // no such option in XBoard (yet)
572 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
576 gtk_widget_get_allocation(shell, &a);
577 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
581 wp->height = a.height;
582 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
583 frameX = 3; frameY = 3; // remember to decide if windows touch
587 GetPlacement (DialogClass dlg, WindowPlacement *wp)
588 { // wrapper to shield back-end from widget type
589 if(shellUp[dlg]) GetActualPlacement(shells[dlg], wp);
594 { // wrapper to shield use of window handles from back-end (make addressible by number?)
595 // In XBoard this will have to wait until awareness of window parameters is implemented
596 GetActualPlacement(shellWidget, &wpMain);
597 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
598 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
599 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
600 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
601 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
602 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
603 GetPlacement(ChatDlg, &wpConsole); if(appData.icsActive) wpConsole.visible = shellUp[ChatDlg];
607 PrintCommPortSettings (FILE *f, char *name)
608 { // This option does not exist in XBoard
612 EnsureOnScreen (int *x, int *y, int minX, int minY)
619 { // [HGM] args: allows testing if main window is realized from back-end
620 return DialogExists(BoardWindow);
624 PopUpStartupDialog ()
625 { // start menu not implemented in XBoard
629 ConvertToLine (int argc, char **argv)
631 static char line[128*1024], buf[1024];
635 for(i=1; i<argc; i++)
637 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
638 && argv[i][0] != '{' )
639 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
641 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
642 strncat(line, buf, 128*1024 - strlen(line) - 1 );
645 line[strlen(line)-1] = NULLCHAR;
649 //--------------------------------------------------------------------------------------------
654 ResizeBoardWindow (int w, int h, int inhibit)
658 // if(clockKludge) return; // ignore as long as clock does not have final height
659 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
661 gtk_widget_get_allocation(shellWidget, &a);
662 marginW = a.width - bw;
663 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
664 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
665 // w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
666 // h += marginH + a.height + 1;
667 gtk_window_resize(GTK_WINDOW(shellWidget), w, 10);
669 if(!appData.fixedSize) gtk_widget_set_size_request(optList[W_BOARD].handle, 100, 100); // liberate board again
674 { // dummy, as the GTK code does not make colors in advance
679 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
680 { // determine what fonts to use, and create them
682 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
683 appData.clockFont = fontTable[CLOCK_FONT][squareSize], fontIsSet[CLOCK_FONT] = True;
684 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
685 appData.font = fontTable[MESSAGE_FONT][squareSize], fontIsSet[MESSAGE_FONT] = True;
686 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
687 appData.coordFont = fontTable[COORD_FONT][squareSize], fontIsSet[COORD_FONT] = True;
688 if(!fontIsSet[CONSOLE_FONT] && fontValid[CONSOLE_FONT][squareSize])
689 appData.icsFont = fontTable[CONSOLE_FONT][squareSize], fontIsSet[CONSOLE_FONT] = True;
690 if(!fontIsSet[EDITTAGS_FONT] && fontValid[EDITTAGS_FONT][squareSize])
691 appData.tagsFont = fontTable[EDITTAGS_FONT][squareSize], fontIsSet[EDITTAGS_FONT] = True;
692 if(!fontIsSet[COMMENT_FONT] && fontValid[COMMENT_FONT][squareSize])
693 appData.commentFont = fontTable[COMMENT_FONT][squareSize], fontIsSet[COMMENT_FONT] = True;
694 if(!fontIsSet[MOVEHISTORY_FONT] && fontValid[MOVEHISTORY_FONT][squareSize])
695 appData.historyFont = fontTable[MOVEHISTORY_FONT][squareSize], fontIsSet[MOVEHISTORY_FONT] = True;
696 if(!fontIsSet[GAMELIST_FONT] && fontValid[GAMELIST_FONT][squareSize])
697 appData.gameListFont = fontTable[GAMELIST_FONT][squareSize], fontIsSet[GAMELIST_FONT] = True;
699 appData.font = InsertPxlSize(appData.font, coordFontPxlSize);
700 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
701 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
702 appData.icsFont = InsertPxlSize(appData.icsFont, coordFontPxlSize);
703 appData.tagsFont = InsertPxlSize(appData.tagsFont, coordFontPxlSize);
704 appData.commentFont = InsertPxlSize(appData.commentFont, coordFontPxlSize);
705 appData.historyFont = InsertPxlSize(appData.historyFont, coordFontPxlSize);
706 appData.gameListFont = InsertPxlSize(appData.gameListFont, coordFontPxlSize);
712 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
713 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
714 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
715 appData.font = fontTable[MESSAGE_FONT][squareSize];
716 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
717 appData.coordFont = fontTable[COORD_FONT][squareSize];
720 appData.font = InsertPxlSize(appData.font, fontPxlSize);
721 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
722 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
723 fontSet = CreateFontSet(appData.font);
724 clockFontSet = CreateFontSet(appData.clockFont);
726 /* For the coordFont, use the 0th font of the fontset. */
727 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
728 XFontStruct **font_struct_list;
729 XFontSetExtents *fontSize;
730 char **font_name_list;
731 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
732 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
733 coordFontStruct = XQueryFont(xDisplay, coordFontID);
734 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
735 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
738 appData.font = FindFont(appData.font, fontPxlSize);
739 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
740 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
741 clockFontID = XLoadFont(xDisplay, appData.clockFont);
742 clockFontStruct = XQueryFont(xDisplay, clockFontID);
743 coordFontID = XLoadFont(xDisplay, appData.coordFont);
744 coordFontStruct = XQueryFont(xDisplay, coordFontID);
745 // textHeight in !NLS mode!
747 countFontID = coordFontID; // [HGM] holdings
748 countFontStruct = coordFontStruct;
750 xdb = XtDatabase(xDisplay);
752 XrmPutLineResource(&xdb, "*international: True");
753 vTo.size = sizeof(XFontSet);
754 vTo.addr = (XtPointer) &fontSet;
755 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
757 XrmPutStringResource(&xdb, "*font", appData.font);
768 case ArgInt: p = " N"; break;
769 case ArgString: p = " STR"; break;
770 case ArgBoolean: p = " TF"; break;
771 case ArgSettingsFilename:
772 case ArgBackupSettingsFile:
773 case ArgFilename: p = " FILE"; break;
774 case ArgX: p = " Nx"; break;
775 case ArgY: p = " Ny"; break;
776 case ArgAttribs: p = " TEXTCOL"; break;
777 case ArgColor: p = " COL"; break;
778 case ArgFont: p = " FONT"; break;
779 case ArgBoardSize: p = " SIZE"; break;
780 case ArgFloat: p = " FLOAT"; break;
785 case ArgCommSettings:
797 ArgDescriptor *q, *p = argDescriptors+5;
798 printf("\nXBoard accepts the following options:\n"
799 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
800 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
801 " SIZE = board-size spec(s)\n"
802 " Within parentheses are short forms, or options to set to true or false.\n"
803 " Persistent options (saved in the settings file) are marked with *)\n\n");
805 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
806 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
807 if(p->save) strcat(buf+len, "*");
808 for(q=p+1; q->argLoc == p->argLoc; q++) {
809 if(q->argName[0] == '-') continue;
810 strcat(buf+len, q == p+1 ? " (" : " ");
811 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
813 if(q != p+1) strcat(buf+len, ")");
815 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
818 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
822 SlaveResize (Option *opt)
824 static int slaveW, slaveH, w, h;
827 gtk_widget_get_allocation(shells[DummyDlg], &a);
828 w = a.width; h = a.height;
829 gtk_widget_get_allocation(opt->handle, &a);
830 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
831 slaveH = h - a.height + 13;
833 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
837 LoadIconFile (gchar *svgFilename)
841 snprintf(buf, MSG_SIZ, "%s/%s" IMG, svgDir, svgFilename);
842 return gdk_pixbuf_new_from_file(buf, NULL);
846 static char clickedFile[MSG_SIZ];
850 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
851 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
854 if(1000*now.sec + now.ms - 1000*started.sec - started.ms < 1000) { // received during first second
855 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
856 } else { // we are running something presumably useful
858 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
859 system(buf); // start new instance on this file
864 GtkosxApplication *theApp;
868 main (int argc, char **argv)
870 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
871 int boardWidth, w, h; //, boardHeight;
873 int forceMono = False;
875 srandom(time(0)); // [HGM] book: make random truly random
877 setbuf(stdout, NULL);
878 setbuf(stderr, NULL);
881 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
882 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
886 if(argc > 1 && !strcmp(argv[1], "--help" )) {
892 gtk_init (&argc, &argv);
894 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
895 char *path = gtkosx_application_get_bundle_path();
897 char *res_path = gtkosx_application_get_resource_path();
898 snprintf(localeDir, MSG_SIZ, "%s/share/locale", res_path); // redefine locale dir for OSX bundle
900 GetTimeMark(&started); // remember start time
901 theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
902 snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
903 snprintf(dataDir, MSG_SIZ, "%s/Contents/Resources/share/xboard", path);
904 snprintf(manDir, MSG_SIZ, "%s/Contents/Resources/share/man", path);
905 snprintf(svgDir, MSG_SIZ, "%s/themes/default", dataDir);
906 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
907 g_signal_connect(theApp, "NSApplicationWillTerminate", G_CALLBACK(ExitEvent), NULL);
908 // we must call application ready before we can get the signal,
909 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
910 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
911 gtkosx_application_ready(theApp);
912 if(argc == 1) { // called without args: OSX open-file signal might follow
913 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
914 usleep(10000); // wait 10 msec (and hope this is long enough).
915 while(gtk_events_pending())
916 gtk_main_iteration(); // process all events that came in upto now
917 if(clickedFile[0]) { // we were sent an open-file signal with filename!
918 fakeArgv[0] = argv[0];
919 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
925 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
926 typedef struct {char *name, *value; } Config;
927 static Config configList[] = {
928 { "Datadir", dataDir },
929 { "Mandir", manDir },
930 { "Sysconfdir", SYSCONFDIR },
935 for(i=0; configList[i].name; i++) {
936 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
937 if(argc > 2) printf("%s", configList[i].value);
938 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
943 /* set up keyboard accelerators group */
944 GtkAccelerators = gtk_accel_group_new();
946 programName = strrchr(argv[0], '/');
947 if (programName == NULL)
948 programName = argv[0];
953 // if (appData.debugMode) {
954 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
957 bindtextdomain(PACKAGE, LOCALEDIR);
958 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
962 appData.boardSize = "";
963 InitAppData(ConvertToLine(argc, argv));
965 if (p == NULL) p = "/tmp";
966 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
967 gameCopyFilename = (char*) malloc(i);
968 gamePasteFilename = (char*) malloc(i);
969 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
970 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
972 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
973 static char buf[MSG_SIZ];
974 snprintf(buf, MSG_SIZ, appData.sysOpen, dataDir);
975 ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
976 EscapeExpand(buf, appData.firstInitString);
977 appData.firstInitString = strdup(buf);
978 EscapeExpand(buf, appData.secondInitString);
979 appData.secondInitString = strdup(buf);
980 EscapeExpand(buf, appData.firstComputerString);
981 appData.firstComputerString = strdup(buf);
982 EscapeExpand(buf, appData.secondComputerString);
983 appData.secondComputerString = strdup(buf);
986 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
987 static char dirName[MSG_SIZ];
988 getcwd(dirName, MSG_SIZ);
991 if (chdir(chessDir) != 0) {
992 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
998 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
999 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1000 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1001 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1004 setbuf(debugFP, NULL);
1008 if (appData.debugMode) {
1009 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1013 /* [HGM,HR] make sure board size is acceptable */
1014 if(appData.NrFiles > BOARD_FILES ||
1015 appData.NrRanks > BOARD_RANKS )
1016 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1019 /* This feature does not work; animation needs a rewrite */
1020 appData.highlightDragging = FALSE;
1024 gameInfo.variant = StringToVariant(appData.variant);
1025 InitPosition(FALSE);
1028 * determine size, based on supplied or remembered -size, or screen size
1030 if (isdigit(appData.boardSize[0])) {
1031 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1032 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1033 &fontPxlSize, &smallLayout, &tinyLayout);
1035 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1036 programName, appData.boardSize);
1040 /* Find some defaults; use the nearest known size */
1041 SizeDefaults *szd, *nearest;
1042 int distance = 99999;
1043 nearest = szd = sizeDefaults;
1044 while (szd->name != NULL) {
1045 if (abs(szd->squareSize - squareSize) < distance) {
1047 distance = abs(szd->squareSize - squareSize);
1048 if (distance == 0) break;
1052 if (i < 2) lineGap = nearest->lineGap;
1053 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1054 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1055 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1056 if (i < 6) smallLayout = nearest->smallLayout;
1057 if (i < 7) tinyLayout = nearest->tinyLayout;
1060 SizeDefaults *szd = sizeDefaults;
1061 if (*appData.boardSize == NULLCHAR) {
1062 // GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow)); // TODO: this does not work, as no mainwindow yet
1063 GdkScreen *screen = gdk_screen_get_default();
1064 guint screenwidth = gdk_screen_get_width(screen);
1065 guint screenheight = gdk_screen_get_height(screen);
1066 while (screenwidth < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1067 screenheight < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1070 if (szd->name == NULL) szd--;
1071 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1073 while (szd->name != NULL &&
1074 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1075 if (szd->name == NULL) {
1076 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1077 programName, appData.boardSize);
1081 squareSize = szd->squareSize;
1082 lineGap = szd->lineGap;
1083 clockFontPxlSize = szd->clockFontPxlSize;
1084 coordFontPxlSize = szd->coordFontPxlSize;
1085 fontPxlSize = szd->fontPxlSize;
1086 smallLayout = szd->smallLayout;
1087 tinyLayout = szd->tinyLayout;
1088 // [HGM] font: use defaults from settings file if available and not overruled
1090 initialSquareSize = squareSize; // [HGM] remember for saving font info
1091 if(BOARD_WIDTH != 8) {
1092 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // keep width the same
1093 lineGap = (squareSize < 37 ? 1 : squareSize < 59 ? 2 : squareSize < 116 ? 3 : 4);
1096 defaultLineGap = lineGap;
1097 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1099 /* [HR] height treated separately (hacked) */
1100 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1101 // boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1104 * Determine what fonts to use.
1106 InitializeFonts((2*clockFontPxlSize+1)/3, coordFontPxlSize, fontPxlSize);
1109 * Detect if there are not enough colors available and adapt.
1112 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1113 appData.monoMode = True;
1117 forceMono = MakeColors();
1120 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1122 appData.monoMode = True;
1125 ParseIcsTextColors();
1131 layoutName = "tinyLayout";
1132 } else if (smallLayout) {
1133 layoutName = "smallLayout";
1135 layoutName = "normalLayout";
1138 if(appData.logoSize) appData.logoSize = boardWidth/4-3;
1139 wpMain.width = -1; // prevent popup sizes window
1140 optList = BoardPopUp(squareSize, lineGap, (void*)
1150 InitDrawingHandle(optList + W_BOARD);
1151 shellWidget = shells[BoardWindow];
1152 currBoard = &optList[W_BOARD];
1153 boardWidget = optList[W_BOARD].handle;
1154 menuBarWidget = optList[W_MENU].handle;
1155 dropMenu = optList[W_DROP].handle;
1156 titleWidget = optList[optList[W_TITLE].type != Skip ? W_TITLE : W_SMALL].handle;
1157 DelayedDrag(); // fake configure event (i3wm tiling window manager fails to send one after initial resize)
1159 formWidget = XtParent(boardWidget);
1160 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1161 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1162 XtGetValues(optList[W_WHITE].handle, args, 2);
1163 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1164 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1165 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1166 XtGetValues(optList[W_PAUSE].handle, args, 2);
1170 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1171 // not need to go into InitDrawingSizes().
1175 // add accelerators to main shell
1176 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1179 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1181 WhiteIcon = LoadIconFile("icon_white");
1182 BlackIcon = LoadIconFile("icon_black");
1183 SetClockIcon(0); // sets white icon
1187 * Create a cursor for the board widget.
1190 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1191 XChangeWindowAttributes(xDisplay, xBoardWindow,
1192 CWCursor, &window_attributes);
1196 * Inhibit shell resizing.
1199 shellArgs[0].value = (XtArgVal) &w;
1200 shellArgs[1].value = (XtArgVal) &h;
1201 XtGetValues(shellWidget, shellArgs, 2);
1202 shellArgs[4].value = shellArgs[2].value = w;
1203 shellArgs[5].value = shellArgs[3].value = h;
1204 // XtSetValues(shellWidget, &shellArgs[2], 4);
1207 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1208 // It wil only become known asynchronously, when we first write a string into it.
1209 // This will then change the clock widget height, which triggers resizing the top-level window
1210 // and a configure event. Only then can we know the total height of the top-level window,
1211 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1212 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1215 gtk_widget_get_allocation(shells[BoardWindow], &a);
1216 w = a.width; h = a.height;
1217 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1218 clockKludge = hc = a.height;
1219 gtk_widget_get_allocation(boardWidget, &a);
1220 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1221 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1227 if(appData.logoSize)
1228 { // locate and read user logo
1229 char buf[MSG_SIZ], name[MSG_SIZ];
1230 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1231 if(!FindLogo(name, ".logo", buf))
1232 FindLogo(appData.logoDir, name + 6, buf);
1233 ASSIGN(userLogo, buf);
1236 if (appData.animate || appData.animateDragging)
1239 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1240 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1242 /* [AS] Restore layout */
1243 if( wpMoveHistory.visible ) {
1247 if( wpEvalGraph.visible )
1252 if( wpEngineOutput.visible ) {
1253 EngineOutputPopUp();
1256 if( wpConsole.visible && appData.icsActive ) {
1261 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1266 if (errorExitStatus == -1) {
1267 if (appData.icsActive) {
1268 /* We now wait until we see "login:" from the ICS before
1269 sending the logon script (problems with timestamp otherwise) */
1270 /*ICSInitScript();*/
1271 if (appData.icsInputBox) ICSInputBoxPopUp();
1275 signal(SIGWINCH, TermSizeSigHandler);
1277 signal(SIGINT, IntSigHandler);
1278 signal(SIGTERM, IntSigHandler);
1279 if (*appData.cmailGameName != NULLCHAR) {
1280 signal(SIGUSR1, CmailSigHandler);
1285 // XtSetKeyboardFocus(shellWidget, formWidget);
1287 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1290 /* check for GTK events and process them */
1293 gtk_main_iteration();
1296 if (appData.debugMode) fclose(debugFP); // [DM] debug
1303 while(gtk_events_pending()) gtk_main_iteration();
1307 TermSizeSigHandler (int sig)
1313 IntSigHandler (int sig)
1319 CmailSigHandler (int sig)
1324 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1326 /* Activate call-back function CmailSigHandlerCallBack() */
1327 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1329 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1333 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1336 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1338 /**** end signal code ****/
1341 #define Abs(n) ((n)<0 ? -(n) : (n))
1344 InsertPxlSize (char *pattern, int targetPxlSize)
1347 snprintf(buf, MSG_SIZ, pattern, targetPxlSize); // pattern is something like "Sans Bold %d"
1354 InsertPxlSize (char *pattern, int targetPxlSize)
1356 char *base_fnt_lst, strInt[12], *p, *q;
1357 int alternatives, i, len, strIntLen;
1360 * Replace the "*" (if present) in the pixel-size slot of each
1361 * alternative with the targetPxlSize.
1365 while ((p = strchr(p, ',')) != NULL) {
1369 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1370 strIntLen = strlen(strInt);
1371 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1375 while (alternatives--) {
1376 char *comma = strchr(p, ',');
1377 for (i=0; i<14; i++) {
1378 char *hyphen = strchr(p, '-');
1380 if (comma && hyphen > comma) break;
1381 len = hyphen + 1 - p;
1382 if (i == 7 && *p == '*' && len == 2) {
1384 memcpy(q, strInt, strIntLen);
1394 len = comma + 1 - p;
1401 return base_fnt_lst;
1407 CreateFontSet (char *base_fnt_lst)
1410 char **missing_list;
1414 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1415 &missing_list, &missing_count, &def_string);
1416 if (appData.debugMode) {
1418 XFontStruct **font_struct_list;
1419 char **font_name_list;
1420 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1422 fprintf(debugFP, " got list %s, locale %s\n",
1423 XBaseFontNameListOfFontSet(fntSet),
1424 XLocaleOfFontSet(fntSet));
1425 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1426 for (i = 0; i < count; i++) {
1427 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1430 for (i = 0; i < missing_count; i++) {
1431 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1434 if (fntSet == NULL) {
1435 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1441 #else // not ENABLE_NLS
1443 * Find a font that matches "pattern" that is as close as
1444 * possible to the targetPxlSize. Prefer fonts that are k
1445 * pixels smaller to fonts that are k pixels larger. The
1446 * pattern must be in the X Consortium standard format,
1447 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1448 * The return value should be freed with XtFree when no
1453 FindFont (char *pattern, int targetPxlSize)
1455 char **fonts, *p, *best, *scalable, *scalableTail;
1456 int i, j, nfonts, minerr, err, pxlSize;
1458 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1460 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1461 programName, pattern);
1468 for (i=0; i<nfonts; i++) {
1471 if (*p != '-') continue;
1473 if (*p == NULLCHAR) break;
1474 if (*p++ == '-') j++;
1476 if (j < 7) continue;
1479 scalable = fonts[i];
1482 err = pxlSize - targetPxlSize;
1483 if (Abs(err) < Abs(minerr) ||
1484 (minerr > 0 && err < 0 && -err == minerr)) {
1490 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1491 /* If the error is too big and there is a scalable font,
1492 use the scalable font. */
1493 int headlen = scalableTail - scalable;
1494 p = (char *) XtMalloc(strlen(scalable) + 10);
1495 while (isdigit(*scalableTail)) scalableTail++;
1496 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1498 p = (char *) XtMalloc(strlen(best) + 2);
1499 safeStrCpy(p, best, strlen(best)+1 );
1501 if (appData.debugMode) {
1502 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1503 pattern, targetPxlSize, p);
1505 XFreeFontNames(fonts);
1512 MarkMenuItem (char *menuRef, int state)
1514 MenuItem *item = MenuNameToItem(menuRef);
1516 if(item && item->handle) {
1517 ((GtkCheckMenuItem *) (item->handle))->active = state;
1523 EnableNamedMenuItem (char *menuRef, int state)
1525 MenuItem *item = MenuNameToItem(menuRef);
1527 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1532 EnableButtonBar (int state)
1535 XtSetSensitive(optList[W_BUTTON].handle, state);
1541 SetMenuEnables (Enables *enab)
1543 while (enab->name != NULL) {
1544 EnableNamedMenuItem(enab->name, enab->value);
1549 gboolean KeyPressProc(window, eventkey, data)
1551 GdkEventKey *eventkey;
1555 MoveTypeInProc(eventkey); // pop up for typed in moves
1558 /* check for other key values */
1559 switch(eventkey->keyval) {
1571 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1572 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1574 if(*nprms == 0) return;
1575 item = MenuNameToItem(prms[0]);
1576 if(item) ((MenuProc *) item->proc) ();
1590 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1591 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1592 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1593 dmEnables[i].piece);
1594 XtSetSensitive(entry, p != NULL || !appData.testLegality
1595 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1596 && !appData.icsActive));
1598 while (p && *p++ == dmEnables[i].piece) count++;
1599 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1601 XtSetArg(args[j], XtNlabel, label); j++;
1602 XtSetValues(entry, args, j);
1608 do_flash_delay (unsigned long msec)
1614 FlashDelay (int flash_delay)
1616 if(flash_delay) do_flash_delay(flash_delay);
1620 Fraction (int x, int start, int stop)
1622 double f = ((double) x - start)/(stop - start);
1623 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1627 static WindowPlacement wpNew;
1630 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1632 int touch=0, fudge = 4, f = 3;
1633 GetActualPlacement(sh, wp);
1634 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1635 if(abs(wp->x + wp->width + 2*frameX - f - wpMain.x) < fudge) touch = 2; else // left touch
1636 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1637 if(abs(wp->y + wp->height + frameX + frameY - f - wpMain.y) < fudge) touch = 4; // top touch
1638 //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);
1639 if(!touch ) return; // only windows that touch co-move
1640 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1641 int heightInc = wpNew.height - wpMain.height;
1642 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1643 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1644 wp->y += fracTop * heightInc;
1645 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1647 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1649 wp->height += heightInc;
1650 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1651 int widthInc = wpNew.width - wpMain.width;
1652 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1653 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1654 wp->y += fracLeft * widthInc;
1655 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1657 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1659 wp->width += widthInc;
1661 wp->x += wpNew.x - wpMain.x;
1662 wp->y += wpNew.y - wpMain.y;
1663 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1664 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1666 XtSetArg(args[j], XtNx, wp->x); j++;
1667 XtSetArg(args[j], XtNy, wp->y); j++;
1668 XtSetValues(sh, args, j);
1670 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1671 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1672 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1676 ReSize (WindowPlacement *wp)
1679 int sqx, sqy, i, w, h, lg = lineGap;
1680 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1681 gtk_widget_get_allocation(optList[W_DROP+1].handle, &a); // table that should contain everything
1682 w = a.width; h = a.height;
1683 gtk_widget_get_allocation(shellWidget, &a);
1684 if(a.width < w || a.height < h) { // outer window smaller than dialog content?
1685 w = a.width - w; h = a.height - h; // subtract matrgins, measured as table minus board dimensions
1686 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1687 w += a.width; h += a.height;
1689 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1690 w = a.width; h = a.height;
1692 sqx = (w - lg) / BOARD_WIDTH - lg;
1693 sqy = (h - lg) / BOARD_HEIGHT - lg;
1694 if(sqy < sqx) sqx = sqy;
1695 if(sqx < 20) return;
1696 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1698 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1699 sqx = (w - lg) / BOARD_WIDTH - lg;
1700 sqy = (h - lg) / BOARD_HEIGHT - lg;
1701 if(sqy < sqx) sqx = sqy;
1702 lg = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1703 if(sqx == oldSqx + 1 && lg == lineGap + 1) sqx = oldSqx, squareSize = 0; // prevent oscillations, force resize by kludge
1705 for(h=0; sizeDefaults[h+1].name && sizeDefaults[h].squareSize*8 > sqx*BOARD_WIDTH; h++) {}
1706 if(initialSquareSize != sizeDefaults[h].squareSize && !appData.fixedSize) { // boardSize changed
1707 initialSquareSize = sizeDefaults[h].squareSize; // used for saving font
1708 ChangeFont(1, &appData.clockFont, CLOCK_FONT, initialSquareSize, CLOCK_FONT_NAME, 2*(sizeDefaults[h].clockFontPxlSize+1)/3);
1709 ChangeFont(1, &appData.font, MESSAGE_FONT, initialSquareSize, DEFAULT_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1710 ChangeFont(0, &appData.icsFont, CONSOLE_FONT, initialSquareSize, CONSOLE_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1711 ChangeFont(0, &appData.tagsFont, EDITTAGS_FONT, initialSquareSize, TAGS_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1712 ChangeFont(0, &appData.commentFont, COMMENT_FONT, initialSquareSize, COMMENT_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1713 ChangeFont(0, &appData.gameListFont, GAMELIST_FONT, initialSquareSize, GAMELIST_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1714 ChangeFont(0, &appData.historyFont, MOVEHISTORY_FONT, initialSquareSize, HISTORY_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1715 DisplayBothClocks();
1716 ApplyFont(&mainOptions[W_MESSG], NULL);
1717 for(i=1; i<6; i++) ApplyFont(&mainOptions[W_BUTTON+i], NULL);
1718 ApplyFont(&tagsOptions[1], NULL);
1719 ApplyFont(&commentOptions[0], NULL);
1720 ApplyFont(&historyOptions[0], NULL);
1721 ApplyFont(&engoutOptions[5], NULL);
1722 ApplyFont(&engoutOptions[12], NULL);
1723 ApplyFont(&chatOptions[11], appData.icsFont);
1724 AppendColorized(&chatOptions[6], NULL, 0); // kludge to replace font tag
1726 if(!strchr(appData.boardSize, ',')) {
1727 ASSIGN(appData.boardSize, sizeDefaults[h].name);
1730 if(sizeDefaults[h].tinyLayout != tinyLayout) { // alter clipping of menu names to conform to board width
1731 int clip = (tinyLayout = sizeDefaults[h].tinyLayout) + 1;
1733 for(h=1; mainOptions[h].type == DropDown; h++) {
1734 strncpy(text, _(mainOptions[h].name), MSG_SIZ);
1735 if(clip != 1) text[clip + (text[clip-1] == '_')] = NULLCHAR;
1736 gtk_menu_item_set_label((GtkMenuItem *) mainOptions[h].handle, text);
1740 if(sqx != squareSize && !appData.fixedSize) {
1741 squareSize = sqx; // adopt new square size
1742 CreatePNGPieces(appData.pieceDirectory); // make newly scaled pieces
1743 InitDrawingSizes(0, 0); // creates grid etc.
1744 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1745 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1746 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1747 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1748 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1749 if(twoBoards && shellUp[DummyDlg]) {
1750 SlavePopUp(); dualOptions[3].max = 0; DoEvents(); // calls SlaveResize, kludge to force assigning new canvas
1751 partnerUp = !partnerUp; flipView = !flipView;
1752 DrawPosition(True, NULL);
1753 partnerUp = !partnerUp; flipView = !flipView;
1757 static guint delayedDragTag = 0;
1763 if(busy++) return; // prevent recursive calling, but remember we missed an event in 'busy'
1765 if(delayedDragTag) g_source_remove(delayedDragTag); // no more timer interrupts from same event!
1769 GetActualPlacement(shellWidget, &wpNew);
1770 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1771 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1772 busy = 0; break; // false alarm
1774 ReSize(&wpNew); // this can be interrupted by other events
1775 if(appData.useStickyWindows) {
1776 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1777 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1778 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1779 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1780 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1783 DrawPosition(True, NULL);
1784 if(busy > 2) busy = 2; // if multiple events were backlogged, only do one more
1791 //printf("old timr = %d\n", delayedDragTag);
1792 if(delayedDragTag) g_source_remove(delayedDragTag);
1793 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1794 //printf("new timr = %d\n", delayedDragTag);
1798 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1800 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1802 wpNew.x = event->configure.x;
1803 wpNew.y = event->configure.y;
1804 wpNew.width = event->configure.width;
1805 wpNew.height = event->configure.height;
1806 // SetWidgetLabel(&mainOptions[W_WHITE], ""); SetWidgetLabel(&mainOptions[W_BLACK], "");
1807 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1813 /* Disable all user input other than deleting the window */
1814 static int frozen = 0;
1820 /* Grab by a widget that doesn't accept input */
1821 gtk_grab_add(optList[W_MESSG].handle);
1825 /* Undo a FreezeUI */
1829 if (!frozen) return;
1830 gtk_grab_remove(optList[W_MESSG].handle);
1837 static int oldPausing = FALSE;
1838 static GameMode oldMode = (GameMode) -1;
1840 if (!boardWidget) return;
1842 if (pausing != oldPausing) {
1843 oldPausing = pausing;
1844 MarkMenuItem("Mode.Pause", pausing);
1846 if (appData.showButtonBar) {
1847 /* Always toggle, don't set. Previous code messes up when
1848 invoked while the button is pressed, as releasing it
1849 toggles the state again. */
1851 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1852 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1856 wname = ModeToWidgetName(oldMode);
1857 if (wname != NULL) {
1858 MarkMenuItem(wname, False);
1860 wname = ModeToWidgetName(gameMode);
1861 if (wname != NULL) {
1862 MarkMenuItem(wname, True);
1864 if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
1865 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1868 /* Maybe all the enables should be handled here, not just this one */
1869 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1871 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1876 * Button/menu procedures
1879 void CopyFileToClipboard(gchar *filename)
1881 gchar *selection_tmp;
1885 FILE* f = fopen(filename, "r");
1888 if (f == NULL) return;
1892 selection_tmp = g_try_malloc(len + 1);
1893 if (selection_tmp == NULL) {
1894 printf("Malloc failed in CopyFileToClipboard\n");
1897 count = fread(selection_tmp, 1, len, f);
1900 g_free(selection_tmp);
1903 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1905 // copy selection_tmp to clipboard
1906 GdkDisplay *gdisp = gdk_display_get_default();
1908 g_free(selection_tmp);
1911 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1912 gtk_clipboard_set_text(cb, selection_tmp, -1);
1913 g_free(selection_tmp);
1917 CopySomething (char *src)
1919 GdkDisplay *gdisp = gdk_display_get_default();
1921 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1922 if (gdisp == NULL) return;
1923 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1924 gtk_clipboard_set_text(cb, src, -1);
1928 PastePositionProc ()
1930 GdkDisplay *gdisp = gdk_display_get_default();
1934 if (gdisp == NULL) return;
1935 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1936 fenstr = gtk_clipboard_wait_for_text(cb);
1937 if (fenstr==NULL) return; // nothing had been selected to copy
1938 EditPositionPasteFEN(fenstr);
1947 guint len=0; int flip = appData.flipView;
1950 // get game from clipboard
1951 GdkDisplay *gdisp = gdk_display_get_default();
1952 if (gdisp == NULL) return;
1953 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1954 text = gtk_clipboard_wait_for_text(cb);
1955 if (text == NULL) return; // nothing to paste
1958 // write to temp file
1959 if (text == NULL || len == 0) {
1960 return; //nothing to paste
1962 f = fopen(gamePasteFilename, "w");
1964 DisplayError(_("Can't open temp file"), errno);
1967 fwrite(text, 1, len, f);
1971 if(!appData.autoFlipView) appData.flipView = flipView;
1972 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1973 appData.flipView = flip;
1980 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1986 void MoveTypeInProc(eventkey)
1987 GdkEventKey *eventkey;
1991 // ingnore if ctrl, alt, or meta is pressed
1992 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1996 buf[0]=eventkey->keyval;
1998 if (eventkey->keyval > 32 && eventkey->keyval < 256 || *buf == 27)
1999 ConsoleAutoPopUp (buf);
2004 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2006 if (!TempBackwardActive) {
2007 TempBackwardActive = True;
2013 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2015 /* Check to see if triggered by a key release event for a repeating key.
2016 * If so the next queued event will be a key press of the same key at the same time */
2017 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2019 XPeekEvent(xDisplay, &next);
2020 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2021 next.xkey.keycode == event->xkey.keycode)
2025 TempBackwardActive = False;
2031 { // called from menu
2034 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"man %s/../man/man6/xboard.6\"' -e 'end tell'", dataDir);
2037 system("xterm -e man xboard &");
2046 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);
2048 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
2056 SetWindowTitle (char *text, char *title, char *icon)
2061 if (appData.titleInWindow) {
2063 XtSetArg(args[i], XtNlabel, text); i++;
2064 XtSetValues(titleWidget, args, i);
2067 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2068 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2069 XtSetValues(shellWidget, args, i);
2070 XSync(xDisplay, False);
2072 if (appData.titleInWindow) {
2073 SetWidgetLabel(titleWidget, text);
2075 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2080 DisplayIcsInteractionTitle (String message)
2083 if (oldICSInteractionTitle == NULL) {
2084 /* Magic to find the old window title, adapted from vim */
2085 char *wina = getenv("WINDOWID");
2087 Window win = (Window) atoi(wina);
2088 Window root, parent, *children;
2089 unsigned int nchildren;
2090 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2092 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2093 if (!XQueryTree(xDisplay, win, &root, &parent,
2094 &children, &nchildren)) break;
2095 if (children) XFree((void *)children);
2096 if (parent == root || parent == 0) break;
2099 XSetErrorHandler(oldHandler);
2101 if (oldICSInteractionTitle == NULL) {
2102 oldICSInteractionTitle = "xterm";
2105 printf("\033]0;%s\007", message);
2111 LockBoardSize (int after)
2113 static char *oldClockFont, *oldMessgFont;
2115 if(oldMessgFont && !strcmp(oldMessgFont, appData.font) &&
2116 oldClockFont && !strcmp(oldClockFont, appData.clockFont) && after < 2) return; // only do something when font changed
2117 w = BOARD_WIDTH*(squareSize + lineGap) + lineGap;
2118 h = BOARD_HEIGHT*(squareSize + lineGap) + lineGap;
2120 ASSIGN(oldClockFont, appData.clockFont);
2121 ASSIGN(oldMessgFont, appData.font);
2122 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
2124 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board
2126 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
2131 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2133 static int twoLines = -1;
2134 GtkWidget *w = (GtkWidget *) opt->handle;
2136 char *markup, two = (appData.logoSize != 0);
2141 strcpy(bgcolor, "black");
2142 strcpy(fgcolor, "white");
2144 strcpy(bgcolor, "white");
2145 strcpy(fgcolor, "black");
2148 appData.lowTimeWarning &&
2149 (timer / 1000) < appData.icsAlarmTime) {
2150 strcpy(fgcolor, appData.lowTimeWarningColor);
2153 if(! partnerUp && two != twoLines) LockBoardSize(2); // lock board size if clock height changes
2155 gdk_color_parse( bgcolor, &col );
2156 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2158 if (appData.clockMode) {
2159 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2160 bgcolor, fgcolor, color, two ? "\n" : " ", TimeString(timer));
2161 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2162 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2164 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2165 bgcolor, fgcolor, color);
2166 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2167 // bgcolor, fgcolor, color);
2169 gtk_label_set_markup(GTK_LABEL(w), markup);
2172 if(!partnerUp && two != twoLines) LockBoardSize(3), twoLines = two;
2175 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2178 SetClockIcon (int color)
2180 GdkPixbuf *pm = *clockIcons[color];
2181 if (mainwindowIcon != pm) {
2182 mainwindowIcon = pm;
2184 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2186 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2191 #define INPUT_SOURCE_BUF_SIZE 8192
2200 char buf[INPUT_SOURCE_BUF_SIZE];
2205 DoInputCallback(io, cond, data)
2210 /* read input from one of the input source (for example a chess program, ICS, etc).
2211 * and call a function that will handle the input
2218 /* All information (callback function, file descriptor, etc) is
2219 * saved in an InputSource structure
2221 InputSource *is = (InputSource *) data;
2223 if (is->lineByLine) {
2224 count = read(is->fd, is->unused,
2225 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2227 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2228 RemoveInputSource(is); // cease reading stdin
2229 stdoutClosed = TRUE; // suppress future output
2232 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2235 is->unused += count;
2237 /* break input into lines and call the callback function on each
2240 while (p < is->unused) {
2241 q = memchr(p, '\n', is->unused - p);
2242 if (q == NULL) break;
2244 (is->func)(is, is->closure, p, q - p, 0);
2247 /* remember not yet used part of the buffer */
2249 while (p < is->unused) {
2254 /* read maximum length of input buffer and send the whole buffer
2255 * to the callback function
2257 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2262 (is->func)(is, is->closure, is->buf, count, error);
2264 return True; // Must return true or the watch will be removed
2267 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2274 GIOChannel *channel;
2275 ChildProc *cp = (ChildProc *) pr;
2277 is = (InputSource *) calloc(1, sizeof(InputSource));
2278 is->lineByLine = lineByLine;
2282 is->fd = fileno(stdin);
2284 is->kind = cp->kind;
2285 is->fd = cp->fdFrom;
2288 is->unused = is->buf;
2292 /* GTK-TODO: will this work on windows?*/
2294 channel = g_io_channel_unix_new(is->fd);
2295 g_io_channel_set_close_on_unref (channel, TRUE);
2296 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2298 is->closure = closure;
2299 return (InputSourceRef) is;
2304 RemoveInputSource(isr)
2307 InputSource *is = (InputSource *) isr;
2309 if (is->sid == 0) return;
2310 g_source_remove(is->sid);
2317 static Boolean frameWaiting;
2320 FrameAlarm (int sig)
2322 frameWaiting = False;
2323 /* In case System-V style signals. Needed?? */
2324 signal(SIGALRM, FrameAlarm);
2328 FrameDelay (int time)
2330 struct itimerval delay;
2333 frameWaiting = True;
2334 signal(SIGALRM, FrameAlarm);
2335 delay.it_interval.tv_sec =
2336 delay.it_value.tv_sec = time / 1000;
2337 delay.it_interval.tv_usec =
2338 delay.it_value.tv_usec = (time % 1000) * 1000;
2339 setitimer(ITIMER_REAL, &delay, NULL);
2340 while (frameWaiting) pause();
2341 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2342 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2343 setitimer(ITIMER_REAL, &delay, NULL);
2350 FrameDelay (int time)
2353 XSync(xDisplay, False);
2355 // gtk_main_iteration_do(False);
2358 usleep(time * 1000);
2364 FindLogo (char *place, char *name, char *buf)
2365 { // check if file exists in given place
2367 if(!place) return 0;
2368 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2369 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2377 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2379 char buf[MSG_SIZ], *logoName = buf;
2380 if(appData.logo[n][0]) {
2381 logoName = appData.logo[n];
2382 } else if(appData.autoLogo) {
2383 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2384 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2385 } else { // engine; cascade
2386 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2387 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2388 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2389 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2393 { ASSIGN(cps->programLogo, logoName); }
2397 UpdateLogos (int displ)
2399 if(optList[W_WHITE-1].handle == NULL) return;
2400 LoadLogo(&first, 0, 0);
2401 LoadLogo(&second, 1, appData.icsActive);
2402 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2406 void FileNamePopUpWrapper(
2417 GtkFileFilter *gtkfilter;
2418 GtkFileFilter *gtkfilter_all;
2420 char fileext[10] = "";
2421 char *result = NULL;
2423 char curDir[MSG_SIZ];
2425 StartDir(filter, NULL); // change to start directory for this file type
2427 if(def && *def && def[strlen(def)-1] == '/') {
2428 getcwd(curDir, MSG_SIZ);
2433 /* make a copy of the filter string, so that strtok can work with it*/
2434 cp = strdup(filter);
2436 /* add filters for file extensions */
2437 gtkfilter = gtk_file_filter_new();
2438 gtkfilter_all = gtk_file_filter_new();
2440 /* one filter to show everything */
2441 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2442 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2444 /* add filter if present */
2445 result = strtok(cp, space);
2446 while( result != NULL ) {
2447 snprintf(fileext,10,"*%s",result);
2448 result = strtok( NULL, space );
2449 gtk_file_filter_add_pattern(gtkfilter, fileext);
2452 /* second filter to only show what's useful */
2453 gtk_file_filter_set_name (gtkfilter,filter);
2455 if (openMode[0] == 'r')
2457 dialog = gtk_file_chooser_dialog_new (label,
2459 GTK_FILE_CHOOSER_ACTION_OPEN,
2460 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2461 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2466 dialog = gtk_file_chooser_dialog_new (label,
2468 GTK_FILE_CHOOSER_ACTION_SAVE,
2469 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2470 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2472 /* add filename suggestions */
2473 if (strlen(def) > 0 )
2474 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2476 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2480 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2481 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2482 /* activate filter */
2483 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2485 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2490 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2493 f = fopen(filename, openMode);
2496 DisplayError(_("Failed to open file"), errno);
2500 /* TODO add indec */
2502 ASSIGN(*name, filename);
2503 ScheduleDelayedEvent(DelayedLoad, 50);
2505 StartDir(filter, filename);
2508 else StartDir(filter, "");
2510 gtk_widget_destroy (dialog);
2513 if(def && *def && def[strlen(def)-1] == '/') chdir(curDir);