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) {
989 if (chdir(chessDir) != 0) {
990 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
996 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
997 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
998 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
999 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1002 setbuf(debugFP, NULL);
1006 if (appData.debugMode) {
1007 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1011 /* [HGM,HR] make sure board size is acceptable */
1012 if(appData.NrFiles > BOARD_FILES ||
1013 appData.NrRanks > BOARD_RANKS )
1014 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1017 /* This feature does not work; animation needs a rewrite */
1018 appData.highlightDragging = FALSE;
1022 gameInfo.variant = StringToVariant(appData.variant);
1023 InitPosition(FALSE);
1026 * determine size, based on supplied or remembered -size, or screen size
1028 if (isdigit(appData.boardSize[0])) {
1029 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1030 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1031 &fontPxlSize, &smallLayout, &tinyLayout);
1033 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1034 programName, appData.boardSize);
1038 /* Find some defaults; use the nearest known size */
1039 SizeDefaults *szd, *nearest;
1040 int distance = 99999;
1041 nearest = szd = sizeDefaults;
1042 while (szd->name != NULL) {
1043 if (abs(szd->squareSize - squareSize) < distance) {
1045 distance = abs(szd->squareSize - squareSize);
1046 if (distance == 0) break;
1050 if (i < 2) lineGap = nearest->lineGap;
1051 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1052 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1053 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1054 if (i < 6) smallLayout = nearest->smallLayout;
1055 if (i < 7) tinyLayout = nearest->tinyLayout;
1058 SizeDefaults *szd = sizeDefaults;
1059 if (*appData.boardSize == NULLCHAR) {
1060 // GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow)); // TODO: this does not work, as no mainwindow yet
1061 GdkScreen *screen = gdk_screen_get_default();
1062 guint screenwidth = gdk_screen_get_width(screen);
1063 guint screenheight = gdk_screen_get_height(screen);
1064 while (screenwidth < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1065 screenheight < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1068 if (szd->name == NULL) szd--;
1069 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1071 while (szd->name != NULL &&
1072 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1073 if (szd->name == NULL) {
1074 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1075 programName, appData.boardSize);
1079 squareSize = szd->squareSize;
1080 lineGap = szd->lineGap;
1081 clockFontPxlSize = szd->clockFontPxlSize;
1082 coordFontPxlSize = szd->coordFontPxlSize;
1083 fontPxlSize = szd->fontPxlSize;
1084 smallLayout = szd->smallLayout;
1085 tinyLayout = szd->tinyLayout;
1086 // [HGM] font: use defaults from settings file if available and not overruled
1088 initialSquareSize = squareSize; // [HGM] remember for saving font info
1089 if(BOARD_WIDTH != 8) {
1090 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // keep width the same
1091 lineGap = (squareSize < 37 ? 1 : squareSize < 59 ? 2 : squareSize < 116 ? 3 : 4);
1094 defaultLineGap = lineGap;
1095 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1097 /* [HR] height treated separately (hacked) */
1098 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1099 // boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1102 * Determine what fonts to use.
1104 InitializeFonts((2*clockFontPxlSize+1)/3, coordFontPxlSize, fontPxlSize);
1107 * Detect if there are not enough colors available and adapt.
1110 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1111 appData.monoMode = True;
1115 forceMono = MakeColors();
1118 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1120 appData.monoMode = True;
1123 ParseIcsTextColors();
1129 layoutName = "tinyLayout";
1130 } else if (smallLayout) {
1131 layoutName = "smallLayout";
1133 layoutName = "normalLayout";
1136 if(appData.logoSize) appData.logoSize = boardWidth/4-3;
1137 wpMain.width = -1; // prevent popup sizes window
1138 optList = BoardPopUp(squareSize, lineGap, (void*)
1148 InitDrawingHandle(optList + W_BOARD);
1149 shellWidget = shells[BoardWindow];
1150 currBoard = &optList[W_BOARD];
1151 boardWidget = optList[W_BOARD].handle;
1152 menuBarWidget = optList[W_MENU].handle;
1153 dropMenu = optList[W_DROP].handle;
1154 titleWidget = optList[optList[W_TITLE].type != Skip ? W_TITLE : W_SMALL].handle;
1155 DelayedDrag(); // fake configure event (i3wm tiling window manager fails to send one after initial resize)
1157 formWidget = XtParent(boardWidget);
1158 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1159 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1160 XtGetValues(optList[W_WHITE].handle, args, 2);
1161 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1162 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1163 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1164 XtGetValues(optList[W_PAUSE].handle, args, 2);
1168 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1169 // not need to go into InitDrawingSizes().
1173 // add accelerators to main shell
1174 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1177 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1179 WhiteIcon = LoadIconFile("icon_white");
1180 BlackIcon = LoadIconFile("icon_black");
1181 SetClockIcon(0); // sets white icon
1185 * Create a cursor for the board widget.
1188 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1189 XChangeWindowAttributes(xDisplay, xBoardWindow,
1190 CWCursor, &window_attributes);
1194 * Inhibit shell resizing.
1197 shellArgs[0].value = (XtArgVal) &w;
1198 shellArgs[1].value = (XtArgVal) &h;
1199 XtGetValues(shellWidget, shellArgs, 2);
1200 shellArgs[4].value = shellArgs[2].value = w;
1201 shellArgs[5].value = shellArgs[3].value = h;
1202 // XtSetValues(shellWidget, &shellArgs[2], 4);
1205 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1206 // It wil only become known asynchronously, when we first write a string into it.
1207 // This will then change the clock widget height, which triggers resizing the top-level window
1208 // and a configure event. Only then can we know the total height of the top-level window,
1209 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1210 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1213 gtk_widget_get_allocation(shells[BoardWindow], &a);
1214 w = a.width; h = a.height;
1215 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1216 clockKludge = hc = a.height;
1217 gtk_widget_get_allocation(boardWidget, &a);
1218 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1219 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1225 if(appData.logoSize)
1226 { // locate and read user logo
1227 char buf[MSG_SIZ], name[MSG_SIZ];
1228 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1229 if(!FindLogo(name, ".logo", buf))
1230 FindLogo(appData.logoDir, name + 6, buf);
1231 ASSIGN(userLogo, buf);
1234 if (appData.animate || appData.animateDragging)
1237 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1238 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1240 /* [AS] Restore layout */
1241 if( wpMoveHistory.visible ) {
1245 if( wpEvalGraph.visible )
1250 if( wpEngineOutput.visible ) {
1251 EngineOutputPopUp();
1254 if( wpConsole.visible && appData.icsActive ) {
1259 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1264 if (errorExitStatus == -1) {
1265 if (appData.icsActive) {
1266 /* We now wait until we see "login:" from the ICS before
1267 sending the logon script (problems with timestamp otherwise) */
1268 /*ICSInitScript();*/
1269 if (appData.icsInputBox) ICSInputBoxPopUp();
1273 signal(SIGWINCH, TermSizeSigHandler);
1275 signal(SIGINT, IntSigHandler);
1276 signal(SIGTERM, IntSigHandler);
1277 if (*appData.cmailGameName != NULLCHAR) {
1278 signal(SIGUSR1, CmailSigHandler);
1283 // XtSetKeyboardFocus(shellWidget, formWidget);
1285 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1288 /* check for GTK events and process them */
1291 gtk_main_iteration();
1294 if (appData.debugMode) fclose(debugFP); // [DM] debug
1301 while(gtk_events_pending()) gtk_main_iteration();
1305 TermSizeSigHandler (int sig)
1311 IntSigHandler (int sig)
1317 CmailSigHandler (int sig)
1322 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1324 /* Activate call-back function CmailSigHandlerCallBack() */
1325 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1327 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1331 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1334 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1336 /**** end signal code ****/
1339 #define Abs(n) ((n)<0 ? -(n) : (n))
1342 InsertPxlSize (char *pattern, int targetPxlSize)
1345 snprintf(buf, MSG_SIZ, pattern, targetPxlSize); // pattern is something like "Sans Bold %d"
1352 InsertPxlSize (char *pattern, int targetPxlSize)
1354 char *base_fnt_lst, strInt[12], *p, *q;
1355 int alternatives, i, len, strIntLen;
1358 * Replace the "*" (if present) in the pixel-size slot of each
1359 * alternative with the targetPxlSize.
1363 while ((p = strchr(p, ',')) != NULL) {
1367 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1368 strIntLen = strlen(strInt);
1369 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1373 while (alternatives--) {
1374 char *comma = strchr(p, ',');
1375 for (i=0; i<14; i++) {
1376 char *hyphen = strchr(p, '-');
1378 if (comma && hyphen > comma) break;
1379 len = hyphen + 1 - p;
1380 if (i == 7 && *p == '*' && len == 2) {
1382 memcpy(q, strInt, strIntLen);
1392 len = comma + 1 - p;
1399 return base_fnt_lst;
1405 CreateFontSet (char *base_fnt_lst)
1408 char **missing_list;
1412 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1413 &missing_list, &missing_count, &def_string);
1414 if (appData.debugMode) {
1416 XFontStruct **font_struct_list;
1417 char **font_name_list;
1418 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1420 fprintf(debugFP, " got list %s, locale %s\n",
1421 XBaseFontNameListOfFontSet(fntSet),
1422 XLocaleOfFontSet(fntSet));
1423 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1424 for (i = 0; i < count; i++) {
1425 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1428 for (i = 0; i < missing_count; i++) {
1429 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1432 if (fntSet == NULL) {
1433 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1439 #else // not ENABLE_NLS
1441 * Find a font that matches "pattern" that is as close as
1442 * possible to the targetPxlSize. Prefer fonts that are k
1443 * pixels smaller to fonts that are k pixels larger. The
1444 * pattern must be in the X Consortium standard format,
1445 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1446 * The return value should be freed with XtFree when no
1451 FindFont (char *pattern, int targetPxlSize)
1453 char **fonts, *p, *best, *scalable, *scalableTail;
1454 int i, j, nfonts, minerr, err, pxlSize;
1456 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1458 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1459 programName, pattern);
1466 for (i=0; i<nfonts; i++) {
1469 if (*p != '-') continue;
1471 if (*p == NULLCHAR) break;
1472 if (*p++ == '-') j++;
1474 if (j < 7) continue;
1477 scalable = fonts[i];
1480 err = pxlSize - targetPxlSize;
1481 if (Abs(err) < Abs(minerr) ||
1482 (minerr > 0 && err < 0 && -err == minerr)) {
1488 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1489 /* If the error is too big and there is a scalable font,
1490 use the scalable font. */
1491 int headlen = scalableTail - scalable;
1492 p = (char *) XtMalloc(strlen(scalable) + 10);
1493 while (isdigit(*scalableTail)) scalableTail++;
1494 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1496 p = (char *) XtMalloc(strlen(best) + 2);
1497 safeStrCpy(p, best, strlen(best)+1 );
1499 if (appData.debugMode) {
1500 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1501 pattern, targetPxlSize, p);
1503 XFreeFontNames(fonts);
1510 MarkMenuItem (char *menuRef, int state)
1512 MenuItem *item = MenuNameToItem(menuRef);
1514 if(item && item->handle) {
1515 ((GtkCheckMenuItem *) (item->handle))->active = state;
1521 EnableNamedMenuItem (char *menuRef, int state)
1523 MenuItem *item = MenuNameToItem(menuRef);
1525 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1530 EnableButtonBar (int state)
1533 XtSetSensitive(optList[W_BUTTON].handle, state);
1539 SetMenuEnables (Enables *enab)
1541 while (enab->name != NULL) {
1542 EnableNamedMenuItem(enab->name, enab->value);
1547 gboolean KeyPressProc(window, eventkey, data)
1549 GdkEventKey *eventkey;
1553 MoveTypeInProc(eventkey); // pop up for typed in moves
1556 /* check for other key values */
1557 switch(eventkey->keyval) {
1569 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1570 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1572 if(*nprms == 0) return;
1573 item = MenuNameToItem(prms[0]);
1574 if(item) ((MenuProc *) item->proc) ();
1588 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1589 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1590 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1591 dmEnables[i].piece);
1592 XtSetSensitive(entry, p != NULL || !appData.testLegality
1593 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1594 && !appData.icsActive));
1596 while (p && *p++ == dmEnables[i].piece) count++;
1597 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1599 XtSetArg(args[j], XtNlabel, label); j++;
1600 XtSetValues(entry, args, j);
1606 do_flash_delay (unsigned long msec)
1612 FlashDelay (int flash_delay)
1614 if(flash_delay) do_flash_delay(flash_delay);
1618 Fraction (int x, int start, int stop)
1620 double f = ((double) x - start)/(stop - start);
1621 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1625 static WindowPlacement wpNew;
1628 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1630 int touch=0, fudge = 4, f = 3;
1631 GetActualPlacement(sh, wp);
1632 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1633 if(abs(wp->x + wp->width + 2*frameX - f - wpMain.x) < fudge) touch = 2; else // left touch
1634 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1635 if(abs(wp->y + wp->height + frameX + frameY - f - wpMain.y) < fudge) touch = 4; // top touch
1636 //printf("CoDrag: touch = %d x=%d w=%d x2=%d w2=%d fx=%d\n", touch, wpMain.x, wpMain.width, wp->x, wp->width, frameX);
1637 if(!touch ) return; // only windows that touch co-move
1638 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1639 int heightInc = wpNew.height - wpMain.height;
1640 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1641 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1642 wp->y += fracTop * heightInc;
1643 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1645 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1647 wp->height += heightInc;
1648 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1649 int widthInc = wpNew.width - wpMain.width;
1650 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1651 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1652 wp->y += fracLeft * widthInc;
1653 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1655 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1657 wp->width += widthInc;
1659 wp->x += wpNew.x - wpMain.x;
1660 wp->y += wpNew.y - wpMain.y;
1661 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1662 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1664 XtSetArg(args[j], XtNx, wp->x); j++;
1665 XtSetArg(args[j], XtNy, wp->y); j++;
1666 XtSetValues(sh, args, j);
1668 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1669 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1670 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1674 ReSize (WindowPlacement *wp)
1677 int sqx, sqy, i, w, h, lg = lineGap;
1678 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1679 gtk_widget_get_allocation(optList[W_DROP+1].handle, &a); // table that should contain everything
1680 w = a.width; h = a.height;
1681 gtk_widget_get_allocation(shellWidget, &a);
1682 if(a.width < w || a.height < h) { // outer window smaller than dialog content?
1683 w = a.width - w; h = a.height - h; // subtract matrgins, measured as table minus board dimensions
1684 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1685 w += a.width; h += a.height;
1687 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1688 w = a.width; h = a.height;
1690 sqx = (w - lg) / BOARD_WIDTH - lg;
1691 sqy = (h - lg) / BOARD_HEIGHT - lg;
1692 if(sqy < sqx) sqx = sqy;
1693 if(sqx < 20) return;
1694 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1696 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1697 sqx = (w - lg) / BOARD_WIDTH - lg;
1698 sqy = (h - lg) / BOARD_HEIGHT - lg;
1699 if(sqy < sqx) sqx = sqy;
1700 lg = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1701 if(sqx == oldSqx + 1 && lg == lineGap + 1) sqx = oldSqx, squareSize = 0; // prevent oscillations, force resize by kludge
1703 for(h=0; sizeDefaults[h+1].name && sizeDefaults[h].squareSize*8 > sqx*BOARD_WIDTH; h++) {}
1704 if(initialSquareSize != sizeDefaults[h].squareSize && !appData.fixedSize) { // boardSize changed
1705 initialSquareSize = sizeDefaults[h].squareSize; // used for saving font
1706 ChangeFont(1, &appData.clockFont, CLOCK_FONT, initialSquareSize, CLOCK_FONT_NAME, 2*(sizeDefaults[h].clockFontPxlSize+1)/3);
1707 ChangeFont(1, &appData.font, MESSAGE_FONT, initialSquareSize, DEFAULT_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1708 ChangeFont(0, &appData.icsFont, CONSOLE_FONT, initialSquareSize, CONSOLE_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1709 ChangeFont(0, &appData.tagsFont, EDITTAGS_FONT, initialSquareSize, TAGS_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1710 ChangeFont(0, &appData.commentFont, COMMENT_FONT, initialSquareSize, COMMENT_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1711 ChangeFont(0, &appData.gameListFont, GAMELIST_FONT, initialSquareSize, GAMELIST_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1712 ChangeFont(0, &appData.historyFont, MOVEHISTORY_FONT, initialSquareSize, HISTORY_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1713 DisplayBothClocks();
1714 ApplyFont(&mainOptions[W_MESSG], NULL);
1715 for(i=1; i<6; i++) ApplyFont(&mainOptions[W_BUTTON+i], NULL);
1716 ApplyFont(&tagsOptions[1], NULL);
1717 ApplyFont(&commentOptions[0], NULL);
1718 ApplyFont(&historyOptions[0], NULL);
1719 ApplyFont(&engoutOptions[5], NULL);
1720 ApplyFont(&engoutOptions[12], NULL);
1721 ApplyFont(&chatOptions[11], appData.icsFont);
1722 AppendColorized(&chatOptions[6], NULL, 0); // kludge to replace font tag
1724 if(!strchr(appData.boardSize, ',')) {
1725 ASSIGN(appData.boardSize, sizeDefaults[h].name);
1728 if(sizeDefaults[h].tinyLayout != tinyLayout) { // alter clipping of menu names to conform to board width
1729 int clip = (tinyLayout = sizeDefaults[h].tinyLayout) + 1;
1731 for(h=1; mainOptions[h].type == DropDown; h++) {
1732 strncpy(text, _(mainOptions[h].name), MSG_SIZ);
1733 if(clip != 1) text[clip + (text[clip-1] == '_')] = NULLCHAR;
1734 gtk_menu_item_set_label((GtkMenuItem *) mainOptions[h].handle, text);
1738 if(sqx != squareSize && !appData.fixedSize) {
1739 squareSize = sqx; // adopt new square size
1740 CreatePNGPieces(appData.pieceDirectory); // make newly scaled pieces
1741 InitDrawingSizes(0, 0); // creates grid etc.
1742 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1743 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1744 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1745 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1746 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1747 if(twoBoards && shellUp[DummyDlg]) {
1748 SlavePopUp(); dualOptions[3].max = 0; DoEvents(); // calls SlaveResize, kludge to force assigning new canvas
1749 partnerUp = !partnerUp; flipView = !flipView;
1750 DrawPosition(True, NULL);
1751 partnerUp = !partnerUp; flipView = !flipView;
1755 static guint delayedDragTag = 0;
1761 if(busy++) return; // prevent recursive calling, but remember we missed an event in 'busy'
1763 if(delayedDragTag) g_source_remove(delayedDragTag); // no more timer interrupts from same event!
1767 GetActualPlacement(shellWidget, &wpNew);
1768 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1769 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1770 busy = 0; break; // false alarm
1772 ReSize(&wpNew); // this can be interrupted by other events
1773 if(appData.useStickyWindows) {
1774 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1775 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1776 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1777 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1778 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1781 DrawPosition(True, NULL);
1782 if(busy > 2) busy = 2; // if multiple events were backlogged, only do one more
1789 //printf("old timr = %d\n", delayedDragTag);
1790 if(delayedDragTag) g_source_remove(delayedDragTag);
1791 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1792 //printf("new timr = %d\n", delayedDragTag);
1796 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1798 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1800 wpNew.x = event->configure.x;
1801 wpNew.y = event->configure.y;
1802 wpNew.width = event->configure.width;
1803 wpNew.height = event->configure.height;
1804 // SetWidgetLabel(&mainOptions[W_WHITE], ""); SetWidgetLabel(&mainOptions[W_BLACK], "");
1805 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1811 /* Disable all user input other than deleting the window */
1812 static int frozen = 0;
1818 /* Grab by a widget that doesn't accept input */
1819 gtk_grab_add(optList[W_MESSG].handle);
1823 /* Undo a FreezeUI */
1827 if (!frozen) return;
1828 gtk_grab_remove(optList[W_MESSG].handle);
1835 static int oldPausing = FALSE;
1836 static GameMode oldMode = (GameMode) -1;
1838 if (!boardWidget) return;
1840 if (pausing != oldPausing) {
1841 oldPausing = pausing;
1842 MarkMenuItem("Mode.Pause", pausing);
1844 if (appData.showButtonBar) {
1845 /* Always toggle, don't set. Previous code messes up when
1846 invoked while the button is pressed, as releasing it
1847 toggles the state again. */
1849 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1850 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1854 wname = ModeToWidgetName(oldMode);
1855 if (wname != NULL) {
1856 MarkMenuItem(wname, False);
1858 wname = ModeToWidgetName(gameMode);
1859 if (wname != NULL) {
1860 MarkMenuItem(wname, True);
1862 if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
1863 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1866 /* Maybe all the enables should be handled here, not just this one */
1867 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1869 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1874 * Button/menu procedures
1877 void CopyFileToClipboard(gchar *filename)
1879 gchar *selection_tmp;
1883 FILE* f = fopen(filename, "r");
1886 if (f == NULL) return;
1890 selection_tmp = g_try_malloc(len + 1);
1891 if (selection_tmp == NULL) {
1892 printf("Malloc failed in CopyFileToClipboard\n");
1895 count = fread(selection_tmp, 1, len, f);
1898 g_free(selection_tmp);
1901 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1903 // copy selection_tmp to clipboard
1904 GdkDisplay *gdisp = gdk_display_get_default();
1906 g_free(selection_tmp);
1909 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1910 gtk_clipboard_set_text(cb, selection_tmp, -1);
1911 g_free(selection_tmp);
1915 CopySomething (char *src)
1917 GdkDisplay *gdisp = gdk_display_get_default();
1919 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1920 if (gdisp == NULL) return;
1921 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1922 gtk_clipboard_set_text(cb, src, -1);
1926 PastePositionProc ()
1928 GdkDisplay *gdisp = gdk_display_get_default();
1932 if (gdisp == NULL) return;
1933 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1934 fenstr = gtk_clipboard_wait_for_text(cb);
1935 if (fenstr==NULL) return; // nothing had been selected to copy
1936 EditPositionPasteFEN(fenstr);
1945 guint len=0; int flip = appData.flipView;
1948 // get game from clipboard
1949 GdkDisplay *gdisp = gdk_display_get_default();
1950 if (gdisp == NULL) return;
1951 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1952 text = gtk_clipboard_wait_for_text(cb);
1953 if (text == NULL) return; // nothing to paste
1956 // write to temp file
1957 if (text == NULL || len == 0) {
1958 return; //nothing to paste
1960 f = fopen(gamePasteFilename, "w");
1962 DisplayError(_("Can't open temp file"), errno);
1965 fwrite(text, 1, len, f);
1969 if(!appData.autoFlipView) appData.flipView = flipView;
1970 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1971 appData.flipView = flip;
1978 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1984 void MoveTypeInProc(eventkey)
1985 GdkEventKey *eventkey;
1989 // ingnore if ctrl, alt, or meta is pressed
1990 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1994 buf[0]=eventkey->keyval;
1996 if (eventkey->keyval > 32 && eventkey->keyval < 256 || *buf == 27)
1997 ConsoleAutoPopUp (buf);
2002 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2004 if (!TempBackwardActive) {
2005 TempBackwardActive = True;
2011 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2013 /* Check to see if triggered by a key release event for a repeating key.
2014 * If so the next queued event will be a key press of the same key at the same time */
2015 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2017 XPeekEvent(xDisplay, &next);
2018 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2019 next.xkey.keycode == event->xkey.keycode)
2023 TempBackwardActive = False;
2029 { // called from menu
2032 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"man %s/../man/man6/xboard.6\"' -e 'end tell'", dataDir);
2035 system("xterm -e man xboard &");
2044 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"info -d %s/../info -f xboard.info\"' -e 'end tell'", dataDir);
2046 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
2054 SetWindowTitle (char *text, char *title, char *icon)
2059 if (appData.titleInWindow) {
2061 XtSetArg(args[i], XtNlabel, text); i++;
2062 XtSetValues(titleWidget, args, i);
2065 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2066 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2067 XtSetValues(shellWidget, args, i);
2068 XSync(xDisplay, False);
2070 if (appData.titleInWindow) {
2071 SetWidgetLabel(titleWidget, text);
2073 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2078 DisplayIcsInteractionTitle (String message)
2081 if (oldICSInteractionTitle == NULL) {
2082 /* Magic to find the old window title, adapted from vim */
2083 char *wina = getenv("WINDOWID");
2085 Window win = (Window) atoi(wina);
2086 Window root, parent, *children;
2087 unsigned int nchildren;
2088 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2090 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2091 if (!XQueryTree(xDisplay, win, &root, &parent,
2092 &children, &nchildren)) break;
2093 if (children) XFree((void *)children);
2094 if (parent == root || parent == 0) break;
2097 XSetErrorHandler(oldHandler);
2099 if (oldICSInteractionTitle == NULL) {
2100 oldICSInteractionTitle = "xterm";
2103 printf("\033]0;%s\007", message);
2109 LockBoardSize (int after)
2111 static char *oldClockFont, *oldMessgFont;
2113 if(oldMessgFont && !strcmp(oldMessgFont, appData.font) &&
2114 oldClockFont && !strcmp(oldClockFont, appData.clockFont) && after < 2) return; // only do something when font changed
2115 w = BOARD_WIDTH*(squareSize + lineGap) + lineGap;
2116 h = BOARD_HEIGHT*(squareSize + lineGap) + lineGap;
2118 ASSIGN(oldClockFont, appData.clockFont);
2119 ASSIGN(oldMessgFont, appData.font);
2120 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
2122 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board
2124 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
2129 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2131 static int twoLines = -1;
2132 GtkWidget *w = (GtkWidget *) opt->handle;
2134 char *markup, two = (appData.logoSize != 0);
2139 strcpy(bgcolor, "black");
2140 strcpy(fgcolor, "white");
2142 strcpy(bgcolor, "white");
2143 strcpy(fgcolor, "black");
2146 appData.lowTimeWarning &&
2147 (timer / 1000) < appData.icsAlarmTime) {
2148 strcpy(fgcolor, appData.lowTimeWarningColor);
2151 if(! partnerUp && two != twoLines) LockBoardSize(2); // lock board size if clock height changes
2153 gdk_color_parse( bgcolor, &col );
2154 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2156 if (appData.clockMode) {
2157 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2158 bgcolor, fgcolor, color, two ? "\n" : " ", TimeString(timer));
2159 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2160 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2162 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2163 bgcolor, fgcolor, color);
2164 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2165 // bgcolor, fgcolor, color);
2167 gtk_label_set_markup(GTK_LABEL(w), markup);
2170 if(!partnerUp && two != twoLines) LockBoardSize(3), twoLines = two;
2173 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2176 SetClockIcon (int color)
2178 GdkPixbuf *pm = *clockIcons[color];
2179 if (mainwindowIcon != pm) {
2180 mainwindowIcon = pm;
2182 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2184 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2189 #define INPUT_SOURCE_BUF_SIZE 8192
2198 char buf[INPUT_SOURCE_BUF_SIZE];
2203 DoInputCallback(io, cond, data)
2208 /* read input from one of the input source (for example a chess program, ICS, etc).
2209 * and call a function that will handle the input
2216 /* All information (callback function, file descriptor, etc) is
2217 * saved in an InputSource structure
2219 InputSource *is = (InputSource *) data;
2221 if (is->lineByLine) {
2222 count = read(is->fd, is->unused,
2223 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2225 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2226 RemoveInputSource(is); // cease reading stdin
2227 stdoutClosed = TRUE; // suppress future output
2230 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2233 is->unused += count;
2235 /* break input into lines and call the callback function on each
2238 while (p < is->unused) {
2239 q = memchr(p, '\n', is->unused - p);
2240 if (q == NULL) break;
2242 (is->func)(is, is->closure, p, q - p, 0);
2245 /* remember not yet used part of the buffer */
2247 while (p < is->unused) {
2252 /* read maximum length of input buffer and send the whole buffer
2253 * to the callback function
2255 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2260 (is->func)(is, is->closure, is->buf, count, error);
2262 return True; // Must return true or the watch will be removed
2265 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2272 GIOChannel *channel;
2273 ChildProc *cp = (ChildProc *) pr;
2275 is = (InputSource *) calloc(1, sizeof(InputSource));
2276 is->lineByLine = lineByLine;
2280 is->fd = fileno(stdin);
2282 is->kind = cp->kind;
2283 is->fd = cp->fdFrom;
2286 is->unused = is->buf;
2290 /* GTK-TODO: will this work on windows?*/
2292 channel = g_io_channel_unix_new(is->fd);
2293 g_io_channel_set_close_on_unref (channel, TRUE);
2294 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2296 is->closure = closure;
2297 return (InputSourceRef) is;
2302 RemoveInputSource(isr)
2305 InputSource *is = (InputSource *) isr;
2307 if (is->sid == 0) return;
2308 g_source_remove(is->sid);
2315 static Boolean frameWaiting;
2318 FrameAlarm (int sig)
2320 frameWaiting = False;
2321 /* In case System-V style signals. Needed?? */
2322 signal(SIGALRM, FrameAlarm);
2326 FrameDelay (int time)
2328 struct itimerval delay;
2331 frameWaiting = True;
2332 signal(SIGALRM, FrameAlarm);
2333 delay.it_interval.tv_sec =
2334 delay.it_value.tv_sec = time / 1000;
2335 delay.it_interval.tv_usec =
2336 delay.it_value.tv_usec = (time % 1000) * 1000;
2337 setitimer(ITIMER_REAL, &delay, NULL);
2338 while (frameWaiting) pause();
2339 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2340 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2341 setitimer(ITIMER_REAL, &delay, NULL);
2348 FrameDelay (int time)
2351 XSync(xDisplay, False);
2353 // gtk_main_iteration_do(False);
2356 usleep(time * 1000);
2362 FindLogo (char *place, char *name, char *buf)
2363 { // check if file exists in given place
2365 if(!place) return 0;
2366 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2367 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2375 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2377 char buf[MSG_SIZ], *logoName = buf;
2378 if(appData.logo[n][0]) {
2379 logoName = appData.logo[n];
2380 } else if(appData.autoLogo) {
2381 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2382 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2383 } else { // engine; cascade
2384 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2385 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2386 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2387 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2391 { ASSIGN(cps->programLogo, logoName); }
2395 UpdateLogos (int displ)
2397 if(optList[W_WHITE-1].handle == NULL) return;
2398 LoadLogo(&first, 0, 0);
2399 LoadLogo(&second, 1, appData.icsActive);
2400 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2404 void FileNamePopUpWrapper(
2415 GtkFileFilter *gtkfilter;
2416 GtkFileFilter *gtkfilter_all;
2418 char fileext[10] = "";
2419 char *result = NULL;
2421 char curDir[MSG_SIZ];
2423 StartDir(filter, NULL); // change to start directory for this file type
2425 if(def && *def && def[strlen(def)-1] == '/') {
2426 getcwd(curDir, MSG_SIZ);
2431 /* make a copy of the filter string, so that strtok can work with it*/
2432 cp = strdup(filter);
2434 /* add filters for file extensions */
2435 gtkfilter = gtk_file_filter_new();
2436 gtkfilter_all = gtk_file_filter_new();
2438 /* one filter to show everything */
2439 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2440 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2442 /* add filter if present */
2443 result = strtok(cp, space);
2444 while( result != NULL ) {
2445 snprintf(fileext,10,"*%s",result);
2446 result = strtok( NULL, space );
2447 gtk_file_filter_add_pattern(gtkfilter, fileext);
2450 /* second filter to only show what's useful */
2451 gtk_file_filter_set_name (gtkfilter,filter);
2453 if (openMode[0] == 'r')
2455 dialog = gtk_file_chooser_dialog_new (label,
2457 GTK_FILE_CHOOSER_ACTION_OPEN,
2458 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2459 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2464 dialog = gtk_file_chooser_dialog_new (label,
2466 GTK_FILE_CHOOSER_ACTION_SAVE,
2467 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2468 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2470 /* add filename suggestions */
2471 if (strlen(def) > 0 )
2472 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2474 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2478 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2479 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2480 /* activate filter */
2481 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2483 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2488 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2491 f = fopen(filename, openMode);
2494 DisplayError(_("Failed to open file"), errno);
2498 /* TODO add indec */
2500 ASSIGN(*name, filename);
2501 ScheduleDelayedEvent(DelayedLoad, 50);
2503 StartDir(filter, filename);
2506 else StartDir(filter, "");
2508 gtk_widget_destroy (dialog);
2511 if(def && *def && def[strlen(def)-1] == '/') chdir(curDir);