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;
260 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
261 int smallLayout = 0, tinyLayout = 0,
262 marginW, marginH, // [HGM] for run-time resizing
263 fromX = -1, fromY = -1, toX, toY, commentUp = False,
264 errorExitStatus = -1, defaultLineGap;
266 Dimension textHeight;
268 char *chessDir, *programName, *programVersion;
269 Boolean alwaysOnTop = False;
270 char *icsTextMenuString;
272 char *firstChessProgramNames;
273 char *secondChessProgramNames;
275 WindowPlacement wpMain;
276 WindowPlacement wpConsole;
277 WindowPlacement wpComment;
278 WindowPlacement wpMoveHistory;
279 WindowPlacement wpEvalGraph;
280 WindowPlacement wpEngineOutput;
281 WindowPlacement wpGameList;
282 WindowPlacement wpTags;
283 WindowPlacement wpDualBoard;
285 /* This magic number is the number of intermediate frames used
286 in each half of the animation. For short moves it's reduced
287 by 1. The total number of frames will be factor * 2 + 1. */
290 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
297 DropMenuEnables dmEnables[] = {
306 XtResource clientResources[] = {
307 { "flashCount", "flashCount", XtRInt, sizeof(int),
308 XtOffset(AppDataPtr, flashCount), XtRImmediate,
309 (XtPointer) FLASH_COUNT },
313 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
314 char globalTranslations[] =
315 ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
316 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
317 :<KeyDown>Return: TempBackwardProc() \n \
318 :<KeyUp>Return: TempForwardProc() \n";
320 char ICSInputTranslations[] =
321 "<Key>Up: UpKeyProc() \n "
322 "<Key>Down: DownKeyProc() \n "
323 "<Key>Return: EnterKeyProc() \n";
325 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
326 // as the widget is destroyed before the up-click can call extend-end
327 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
330 String xboardResources[] = {
331 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
339 gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
342 //---------------------------------------------------------------------------------------------------------
343 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
346 #define CW_USEDEFAULT (1<<31)
347 #define ICS_TEXT_MENU_SIZE 90
348 #define DEBUG_FILE "xboard.debug"
349 #define SetCurrentDirectory chdir
350 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
354 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
357 // front-end part of option handling
359 // [HGM] This platform-dependent table provides the location for storing the color info
360 extern char *crWhite, * crBlack;
364 &appData.whitePieceColor,
365 &appData.blackPieceColor,
366 &appData.lightSquareColor,
367 &appData.darkSquareColor,
368 &appData.highlightSquareColor,
369 &appData.premoveHighlightColor,
370 &appData.lowTimeWarningColor,
381 // [HGM] font: keep a font for each square size, even non-stndard ones
383 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
384 char *fontTable[NUM_FONTS][MAX_SIZE];
387 ParseFont (char *name, int number)
388 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
390 if(sscanf(name, "size%d:", &size)) {
391 // [HGM] font: font is meant for specific boardSize (likely from settings file);
392 // defer processing it until we know if it matches our board size
393 if(!strstr(name, "-*-") && // ignore X-fonts
394 size >= 0 && size<MAX_SIZE) { // for now, fixed limit
395 fontTable[number][size] = strdup(strchr(name, ':')+1);
396 fontValid[number][size] = True;
401 case 0: // CLOCK_FONT
402 appData.clockFont = strdup(name);
404 case 1: // MESSAGE_FONT
405 appData.font = strdup(name);
407 case 2: // COORD_FONT
408 appData.coordFont = strdup(name);
411 appData.icsFont = strdup(name);
414 appData.tagsFont = strdup(name);
417 appData.commentFont = strdup(name);
419 case MOVEHISTORY_FONT:
420 appData.historyFont = strdup(name);
423 appData.gameListFont = strdup(name);
428 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
433 { // only 2 fonts currently
434 appData.clockFont = strdup(CLOCK_FONT_NAME);
435 appData.coordFont = strdup(COORD_FONT_NAME);
436 appData.font = strdup(DEFAULT_FONT_NAME);
437 appData.icsFont = strdup(CONSOLE_FONT_NAME);
438 appData.tagsFont = strdup(TAGS_FONT_NAME);
439 appData.commentFont = strdup(COMMENT_FONT_NAME);
440 appData.historyFont = strdup(HISTORY_FONT_NAME);
441 appData.gameListFont = strdup(GAMELIST_FONT_NAME);
445 ChangeFont (int force, char **font, int fnr, int size, char *def, int pix)
447 if(!fontValid[fnr][size]) {
448 if(fontIsSet[fnr] && !force) return; // unless forced we do not replace an explicitly specified font by a default
449 ASSIGN(fontTable[fnr][size], def); // use default
450 fontIsSet[fnr] = False;
451 } else fontIsSet[fnr] = True;
452 FREE(*font); *font = InsertPxlSize(fontTable[fnr][size], pix);
457 { // no-op, until we identify the code for this already in XBoard and move it here
461 ParseColor (int n, char *name)
462 { // in XBoard, just copy the color-name string
463 if(colorVariable[n] && *name == '#') *(char**)colorVariable[n] = strdup(name);
469 return *(char**)colorVariable[n];
473 ParseTextAttribs (ColorClass cc, char *s)
475 (&appData.colorShout)[cc] = strdup(s);
479 ParseBoardSize (void *addr, char *name)
481 appData.boardSize = strdup(name);
486 { // In XBoard the sound-playing program takes care of obtaining the actual sound
490 SetCommPortDefaults ()
491 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
494 // [HGM] args: these three cases taken out to stay in front-end
496 SaveFontArg (FILE *f, ArgDescriptor *ad)
499 int i, n = (int)(intptr_t)ad->argLoc;
501 case 0: // CLOCK_FONT
502 name = appData.clockFont;
504 case 1: // MESSAGE_FONT
507 case 2: // COORD_FONT
508 name = appData.coordFont;
511 name = appData.icsFont;
514 name = appData.tagsFont;
517 name = appData.commentFont;
519 case MOVEHISTORY_FONT:
520 name = appData.historyFont;
523 name = appData.gameListFont;
528 if(fontIsSet[n]) // only save fonts that were not defaults
529 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
530 if(sizeDefaults[i].squareSize == initialSquareSize) { // only for standard sizes!
531 ASSIGN(fontTable[n][initialSquareSize], name);
532 fontValid[n][initialSquareSize] = True;
535 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
536 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
541 { // nothing to do, as the sounds are at all times represented by their text-string names already
545 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
546 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
547 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
551 SaveColor (FILE *f, ArgDescriptor *ad)
552 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
553 if(colorVariable[(int)(intptr_t)ad->argLoc])
554 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
558 SaveBoardSize (FILE *f, char *name, void *addr)
559 { // wrapper to shield back-end from BoardSize & sizeInfo
560 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
564 ParseCommPortSettings (char *s)
565 { // no such option in XBoard (yet)
571 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
575 gtk_widget_get_allocation(shell, &a);
576 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
580 wp->height = a.height;
581 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
582 frameX = 3; frameY = 3; // remember to decide if windows touch
586 GetPlacement (DialogClass dlg, WindowPlacement *wp)
587 { // wrapper to shield back-end from widget type
588 if(shellUp[dlg]) GetActualPlacement(shells[dlg], wp);
593 { // wrapper to shield use of window handles from back-end (make addressible by number?)
594 // In XBoard this will have to wait until awareness of window parameters is implemented
595 GetActualPlacement(shellWidget, &wpMain);
596 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
597 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
598 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
599 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
600 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
601 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
602 GetPlacement(ChatDlg, &wpConsole); if(appData.icsActive) wpConsole.visible = shellUp[ChatDlg];
606 PrintCommPortSettings (FILE *f, char *name)
607 { // This option does not exist in XBoard
611 EnsureOnScreen (int *x, int *y, int minX, int minY)
618 { // [HGM] args: allows testing if main window is realized from back-end
619 return DialogExists(BoardWindow);
623 PopUpStartupDialog ()
624 { // start menu not implemented in XBoard
628 ConvertToLine (int argc, char **argv)
630 static char line[128*1024], buf[1024];
634 for(i=1; i<argc; i++)
636 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
637 && argv[i][0] != '{' )
638 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
640 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
641 strncat(line, buf, 128*1024 - strlen(line) - 1 );
644 line[strlen(line)-1] = NULLCHAR;
648 //--------------------------------------------------------------------------------------------
653 ResizeBoardWindow (int w, int h, int inhibit)
657 // if(clockKludge) return; // ignore as long as clock does not have final height
658 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
660 gtk_widget_get_allocation(shellWidget, &a);
661 marginW = a.width - bw;
662 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
663 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
664 // w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
665 // h += marginH + a.height + 1;
666 gtk_window_resize(GTK_WINDOW(shellWidget), w, 10);
668 if(!appData.fixedSize) gtk_widget_set_size_request(optList[W_BOARD].handle, 100, 100); // liberate board again
673 { // dummy, as the GTK code does not make colors in advance
678 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
679 { // determine what fonts to use, and create them
681 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
682 appData.clockFont = fontTable[CLOCK_FONT][squareSize], fontIsSet[CLOCK_FONT] = True;
683 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
684 appData.font = fontTable[MESSAGE_FONT][squareSize], fontIsSet[MESSAGE_FONT] = True;
685 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
686 appData.coordFont = fontTable[COORD_FONT][squareSize], fontIsSet[COORD_FONT] = True;
687 if(!fontIsSet[CONSOLE_FONT] && fontValid[CONSOLE_FONT][squareSize])
688 appData.icsFont = fontTable[CONSOLE_FONT][squareSize], fontIsSet[CONSOLE_FONT] = True;
689 if(!fontIsSet[EDITTAGS_FONT] && fontValid[EDITTAGS_FONT][squareSize])
690 appData.tagsFont = fontTable[EDITTAGS_FONT][squareSize], fontIsSet[EDITTAGS_FONT] = True;
691 if(!fontIsSet[COMMENT_FONT] && fontValid[COMMENT_FONT][squareSize])
692 appData.commentFont = fontTable[COMMENT_FONT][squareSize], fontIsSet[COMMENT_FONT] = True;
693 if(!fontIsSet[MOVEHISTORY_FONT] && fontValid[MOVEHISTORY_FONT][squareSize])
694 appData.historyFont = fontTable[MOVEHISTORY_FONT][squareSize], fontIsSet[MOVEHISTORY_FONT] = True;
695 if(!fontIsSet[GAMELIST_FONT] && fontValid[GAMELIST_FONT][squareSize])
696 appData.gameListFont = fontTable[GAMELIST_FONT][squareSize], fontIsSet[GAMELIST_FONT] = True;
698 appData.font = InsertPxlSize(appData.font, coordFontPxlSize);
699 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
700 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
701 appData.icsFont = InsertPxlSize(appData.icsFont, coordFontPxlSize);
702 appData.tagsFont = InsertPxlSize(appData.tagsFont, coordFontPxlSize);
703 appData.commentFont = InsertPxlSize(appData.commentFont, coordFontPxlSize);
704 appData.historyFont = InsertPxlSize(appData.historyFont, coordFontPxlSize);
705 appData.gameListFont = InsertPxlSize(appData.gameListFont, coordFontPxlSize);
711 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
712 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
713 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
714 appData.font = fontTable[MESSAGE_FONT][squareSize];
715 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
716 appData.coordFont = fontTable[COORD_FONT][squareSize];
719 appData.font = InsertPxlSize(appData.font, fontPxlSize);
720 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
721 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
722 fontSet = CreateFontSet(appData.font);
723 clockFontSet = CreateFontSet(appData.clockFont);
725 /* For the coordFont, use the 0th font of the fontset. */
726 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
727 XFontStruct **font_struct_list;
728 XFontSetExtents *fontSize;
729 char **font_name_list;
730 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
731 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
732 coordFontStruct = XQueryFont(xDisplay, coordFontID);
733 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
734 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
737 appData.font = FindFont(appData.font, fontPxlSize);
738 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
739 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
740 clockFontID = XLoadFont(xDisplay, appData.clockFont);
741 clockFontStruct = XQueryFont(xDisplay, clockFontID);
742 coordFontID = XLoadFont(xDisplay, appData.coordFont);
743 coordFontStruct = XQueryFont(xDisplay, coordFontID);
744 // textHeight in !NLS mode!
746 countFontID = coordFontID; // [HGM] holdings
747 countFontStruct = coordFontStruct;
749 xdb = XtDatabase(xDisplay);
751 XrmPutLineResource(&xdb, "*international: True");
752 vTo.size = sizeof(XFontSet);
753 vTo.addr = (XtPointer) &fontSet;
754 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
756 XrmPutStringResource(&xdb, "*font", appData.font);
767 case ArgInt: p = " N"; break;
768 case ArgString: p = " STR"; break;
769 case ArgBoolean: p = " TF"; break;
770 case ArgSettingsFilename:
771 case ArgBackupSettingsFile:
772 case ArgFilename: p = " FILE"; break;
773 case ArgX: p = " Nx"; break;
774 case ArgY: p = " Ny"; break;
775 case ArgAttribs: p = " TEXTCOL"; break;
776 case ArgColor: p = " COL"; break;
777 case ArgFont: p = " FONT"; break;
778 case ArgBoardSize: p = " SIZE"; break;
779 case ArgFloat: p = " FLOAT"; break;
784 case ArgCommSettings:
796 ArgDescriptor *q, *p = argDescriptors+5;
797 printf("\nXBoard accepts the following options:\n"
798 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
799 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
800 " SIZE = board-size spec(s)\n"
801 " Within parentheses are short forms, or options to set to true or false.\n"
802 " Persistent options (saved in the settings file) are marked with *)\n\n");
804 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
805 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
806 if(p->save) strcat(buf+len, "*");
807 for(q=p+1; q->argLoc == p->argLoc; q++) {
808 if(q->argName[0] == '-') continue;
809 strcat(buf+len, q == p+1 ? " (" : " ");
810 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
812 if(q != p+1) strcat(buf+len, ")");
814 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
817 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
821 SlaveResize (Option *opt)
823 static int slaveW, slaveH, w, h;
826 gtk_widget_get_allocation(shells[DummyDlg], &a);
827 w = a.width; h = a.height;
828 gtk_widget_get_allocation(opt->handle, &a);
829 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
830 slaveH = h - a.height + 13;
832 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
836 LoadIconFile (gchar *svgFilename)
840 snprintf(buf, MSG_SIZ, "%s/%s" IMG, svgDir, svgFilename);
841 return gdk_pixbuf_new_from_file(buf, NULL);
845 static char clickedFile[MSG_SIZ];
849 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
850 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
853 if(1000*now.sec + now.ms - 1000*started.sec - started.ms < 1000) { // received during first second
854 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
855 } else { // we are running something presumably useful
857 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
858 system(buf); // start new instance on this file
863 GtkosxApplication *theApp;
867 main (int argc, char **argv)
869 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
870 int boardWidth, w, h; //, boardHeight;
872 int forceMono = False;
874 srandom(time(0)); // [HGM] book: make random truly random
876 setbuf(stdout, NULL);
877 setbuf(stderr, NULL);
880 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
881 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
885 if(argc > 1 && !strcmp(argv[1], "--help" )) {
891 gtk_init (&argc, &argv);
893 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
894 char *path = gtkosx_application_get_bundle_path();
896 char *res_path = gtkosx_application_get_resource_path();
897 snprintf(localeDir, MSG_SIZ, "%s/share/locale", res_path); // redefine locale dir for OSX bundle
899 GetTimeMark(&started); // remember start time
900 theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
901 snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
902 snprintf(dataDir, MSG_SIZ, "%s/Contents/Resources/share/xboard", path);
903 snprintf(manDir, MSG_SIZ, "%s/Contents/Resources/share/man", path);
904 snprintf(svgDir, MSG_SIZ, "%s/themes/default", dataDir);
905 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
906 g_signal_connect(theApp, "NSApplicationWillTerminate", G_CALLBACK(ExitEvent), NULL);
907 // we must call application ready before we can get the signal,
908 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
909 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
910 gtkosx_application_ready(theApp);
911 if(argc == 1) { // called without args: OSX open-file signal might follow
912 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
913 usleep(10000); // wait 10 msec (and hope this is long enough).
914 while(gtk_events_pending())
915 gtk_main_iteration(); // process all events that came in upto now
916 if(clickedFile[0]) { // we were sent an open-file signal with filename!
917 fakeArgv[0] = argv[0];
918 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
924 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
925 typedef struct {char *name, *value; } Config;
926 static Config configList[] = {
927 { "Datadir", dataDir },
928 { "Mandir", manDir },
929 { "Sysconfdir", SYSCONFDIR },
934 for(i=0; configList[i].name; i++) {
935 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
936 if(argc > 2) printf("%s", configList[i].value);
937 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
942 /* set up keyboard accelerators group */
943 GtkAccelerators = gtk_accel_group_new();
945 programName = strrchr(argv[0], '/');
946 if (programName == NULL)
947 programName = argv[0];
952 // if (appData.debugMode) {
953 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
956 bindtextdomain(PACKAGE, LOCALEDIR);
957 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
961 appData.boardSize = "";
962 InitAppData(ConvertToLine(argc, argv));
964 if (p == NULL) p = "/tmp";
965 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
966 gameCopyFilename = (char*) malloc(i);
967 gamePasteFilename = (char*) malloc(i);
968 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
969 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
971 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
972 static char buf[MSG_SIZ];
973 snprintf(buf, MSG_SIZ, appData.sysOpen, dataDir);
974 ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
975 EscapeExpand(buf, appData.firstInitString);
976 appData.firstInitString = strdup(buf);
977 EscapeExpand(buf, appData.secondInitString);
978 appData.secondInitString = strdup(buf);
979 EscapeExpand(buf, appData.firstComputerString);
980 appData.firstComputerString = strdup(buf);
981 EscapeExpand(buf, appData.secondComputerString);
982 appData.secondComputerString = strdup(buf);
985 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
986 static char dirName[MSG_SIZ];
987 getcwd(dirName, MSG_SIZ);
990 if (chdir(chessDir) != 0) {
991 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
997 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
998 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
999 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1000 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1003 setbuf(debugFP, NULL);
1007 if (appData.debugMode) {
1008 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1012 /* [HGM,HR] make sure board size is acceptable */
1013 if(appData.NrFiles > BOARD_FILES ||
1014 appData.NrRanks > BOARD_RANKS )
1015 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1018 /* This feature does not work; animation needs a rewrite */
1019 appData.highlightDragging = FALSE;
1023 gameInfo.variant = StringToVariant(appData.variant);
1024 InitPosition(FALSE);
1027 * determine size, based on supplied or remembered -size, or screen size
1029 if (isdigit(appData.boardSize[0])) {
1030 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1031 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1032 &fontPxlSize, &smallLayout, &tinyLayout);
1034 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1035 programName, appData.boardSize);
1039 /* Find some defaults; use the nearest known size */
1040 SizeDefaults *szd, *nearest;
1041 int distance = 99999;
1042 nearest = szd = sizeDefaults;
1043 while (szd->name != NULL) {
1044 if (abs(szd->squareSize - squareSize) < distance) {
1046 distance = abs(szd->squareSize - squareSize);
1047 if (distance == 0) break;
1051 if (i < 2) lineGap = nearest->lineGap;
1052 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1053 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1054 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1055 if (i < 6) smallLayout = nearest->smallLayout;
1056 if (i < 7) tinyLayout = nearest->tinyLayout;
1059 SizeDefaults *szd = sizeDefaults;
1060 if (*appData.boardSize == NULLCHAR) {
1061 // GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow)); // TODO: this does not work, as no mainwindow yet
1062 GdkScreen *screen = gdk_screen_get_default();
1063 guint screenwidth = gdk_screen_get_width(screen);
1064 guint screenheight = gdk_screen_get_height(screen);
1065 while (screenwidth < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1066 screenheight < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1069 if (szd->name == NULL) szd--;
1070 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1072 while (szd->name != NULL &&
1073 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1074 if (szd->name == NULL) {
1075 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1076 programName, appData.boardSize);
1080 squareSize = szd->squareSize;
1081 lineGap = szd->lineGap;
1082 clockFontPxlSize = szd->clockFontPxlSize;
1083 coordFontPxlSize = szd->coordFontPxlSize;
1084 fontPxlSize = szd->fontPxlSize;
1085 smallLayout = szd->smallLayout;
1086 tinyLayout = szd->tinyLayout;
1087 // [HGM] font: use defaults from settings file if available and not overruled
1089 initialSquareSize = squareSize; // [HGM] remember for saving font info
1090 if(BOARD_WIDTH != 8) {
1091 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // keep width the same
1092 lineGap = (squareSize < 37 ? 1 : squareSize < 59 ? 2 : squareSize < 116 ? 3 : 4);
1095 defaultLineGap = lineGap;
1096 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1098 /* [HR] height treated separately (hacked) */
1099 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1100 // boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1103 * Determine what fonts to use.
1105 InitializeFonts((2*clockFontPxlSize+1)/3, coordFontPxlSize, fontPxlSize);
1108 * Detect if there are not enough colors available and adapt.
1111 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1112 appData.monoMode = True;
1116 forceMono = MakeColors();
1119 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1121 appData.monoMode = True;
1124 ParseIcsTextColors();
1130 layoutName = "tinyLayout";
1131 } else if (smallLayout) {
1132 layoutName = "smallLayout";
1134 layoutName = "normalLayout";
1137 if(appData.logoSize) appData.logoSize = boardWidth/4-3;
1138 wpMain.width = -1; // prevent popup sizes window
1139 optList = BoardPopUp(squareSize, lineGap, (void*)
1149 InitDrawingHandle(optList + W_BOARD);
1150 shellWidget = shells[BoardWindow];
1151 currBoard = &optList[W_BOARD];
1152 boardWidget = optList[W_BOARD].handle;
1153 menuBarWidget = optList[W_MENU].handle;
1154 dropMenu = optList[W_DROP].handle;
1155 titleWidget = optList[optList[W_TITLE].type != Skip ? W_TITLE : W_SMALL].handle;
1156 DelayedDrag(); // fake configure event (i3wm tiling window manager fails to send one after initial resize)
1158 formWidget = XtParent(boardWidget);
1159 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1160 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1161 XtGetValues(optList[W_WHITE].handle, args, 2);
1162 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1163 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1164 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1165 XtGetValues(optList[W_PAUSE].handle, args, 2);
1169 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1170 // not need to go into InitDrawingSizes().
1174 // add accelerators to main shell
1175 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1178 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1180 WhiteIcon = LoadIconFile("icon_white");
1181 BlackIcon = LoadIconFile("icon_black");
1182 SetClockIcon(0); // sets white icon
1186 * Create a cursor for the board widget.
1189 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1190 XChangeWindowAttributes(xDisplay, xBoardWindow,
1191 CWCursor, &window_attributes);
1195 * Inhibit shell resizing.
1198 shellArgs[0].value = (XtArgVal) &w;
1199 shellArgs[1].value = (XtArgVal) &h;
1200 XtGetValues(shellWidget, shellArgs, 2);
1201 shellArgs[4].value = shellArgs[2].value = w;
1202 shellArgs[5].value = shellArgs[3].value = h;
1203 // XtSetValues(shellWidget, &shellArgs[2], 4);
1206 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1207 // It wil only become known asynchronously, when we first write a string into it.
1208 // This will then change the clock widget height, which triggers resizing the top-level window
1209 // and a configure event. Only then can we know the total height of the top-level window,
1210 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1211 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1214 gtk_widget_get_allocation(shells[BoardWindow], &a);
1215 w = a.width; h = a.height;
1216 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1217 clockKludge = hc = a.height;
1218 gtk_widget_get_allocation(boardWidget, &a);
1219 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1220 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1226 if(appData.logoSize)
1227 { // locate and read user logo
1228 char buf[MSG_SIZ], name[MSG_SIZ];
1229 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1230 if(!FindLogo(name, ".logo", buf))
1231 FindLogo(appData.logoDir, name + 6, buf);
1232 ASSIGN(userLogo, buf);
1235 if (appData.animate || appData.animateDragging)
1238 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1239 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1241 /* [AS] Restore layout */
1242 if( wpMoveHistory.visible ) {
1246 if( wpEvalGraph.visible )
1251 if( wpEngineOutput.visible ) {
1252 EngineOutputPopUp();
1255 if( wpConsole.visible && appData.icsActive ) {
1260 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1265 if (errorExitStatus == -1) {
1266 if (appData.icsActive) {
1267 /* We now wait until we see "login:" from the ICS before
1268 sending the logon script (problems with timestamp otherwise) */
1269 /*ICSInitScript();*/
1270 if (appData.icsInputBox) ICSInputBoxPopUp();
1274 signal(SIGWINCH, TermSizeSigHandler);
1276 signal(SIGINT, IntSigHandler);
1277 signal(SIGTERM, IntSigHandler);
1278 if (*appData.cmailGameName != NULLCHAR) {
1279 signal(SIGUSR1, CmailSigHandler);
1284 // XtSetKeyboardFocus(shellWidget, formWidget);
1286 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1289 /* check for GTK events and process them */
1292 gtk_main_iteration();
1295 if (appData.debugMode) fclose(debugFP); // [DM] debug
1302 while(gtk_events_pending()) gtk_main_iteration();
1306 TermSizeSigHandler (int sig)
1312 IntSigHandler (int sig)
1318 CmailSigHandler (int sig)
1323 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1325 /* Activate call-back function CmailSigHandlerCallBack() */
1326 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1328 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1332 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1335 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1337 /**** end signal code ****/
1340 #define Abs(n) ((n)<0 ? -(n) : (n))
1343 InsertPxlSize (char *pattern, int targetPxlSize)
1346 snprintf(buf, MSG_SIZ, pattern, targetPxlSize); // pattern is something like "Sans Bold %d"
1353 InsertPxlSize (char *pattern, int targetPxlSize)
1355 char *base_fnt_lst, strInt[12], *p, *q;
1356 int alternatives, i, len, strIntLen;
1359 * Replace the "*" (if present) in the pixel-size slot of each
1360 * alternative with the targetPxlSize.
1364 while ((p = strchr(p, ',')) != NULL) {
1368 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1369 strIntLen = strlen(strInt);
1370 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1374 while (alternatives--) {
1375 char *comma = strchr(p, ',');
1376 for (i=0; i<14; i++) {
1377 char *hyphen = strchr(p, '-');
1379 if (comma && hyphen > comma) break;
1380 len = hyphen + 1 - p;
1381 if (i == 7 && *p == '*' && len == 2) {
1383 memcpy(q, strInt, strIntLen);
1393 len = comma + 1 - p;
1400 return base_fnt_lst;
1406 CreateFontSet (char *base_fnt_lst)
1409 char **missing_list;
1413 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1414 &missing_list, &missing_count, &def_string);
1415 if (appData.debugMode) {
1417 XFontStruct **font_struct_list;
1418 char **font_name_list;
1419 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1421 fprintf(debugFP, " got list %s, locale %s\n",
1422 XBaseFontNameListOfFontSet(fntSet),
1423 XLocaleOfFontSet(fntSet));
1424 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1425 for (i = 0; i < count; i++) {
1426 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1429 for (i = 0; i < missing_count; i++) {
1430 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1433 if (fntSet == NULL) {
1434 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1440 #else // not ENABLE_NLS
1442 * Find a font that matches "pattern" that is as close as
1443 * possible to the targetPxlSize. Prefer fonts that are k
1444 * pixels smaller to fonts that are k pixels larger. The
1445 * pattern must be in the X Consortium standard format,
1446 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1447 * The return value should be freed with XtFree when no
1452 FindFont (char *pattern, int targetPxlSize)
1454 char **fonts, *p, *best, *scalable, *scalableTail;
1455 int i, j, nfonts, minerr, err, pxlSize;
1457 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1459 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1460 programName, pattern);
1467 for (i=0; i<nfonts; i++) {
1470 if (*p != '-') continue;
1472 if (*p == NULLCHAR) break;
1473 if (*p++ == '-') j++;
1475 if (j < 7) continue;
1478 scalable = fonts[i];
1481 err = pxlSize - targetPxlSize;
1482 if (Abs(err) < Abs(minerr) ||
1483 (minerr > 0 && err < 0 && -err == minerr)) {
1489 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1490 /* If the error is too big and there is a scalable font,
1491 use the scalable font. */
1492 int headlen = scalableTail - scalable;
1493 p = (char *) XtMalloc(strlen(scalable) + 10);
1494 while (isdigit(*scalableTail)) scalableTail++;
1495 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1497 p = (char *) XtMalloc(strlen(best) + 2);
1498 safeStrCpy(p, best, strlen(best)+1 );
1500 if (appData.debugMode) {
1501 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1502 pattern, targetPxlSize, p);
1504 XFreeFontNames(fonts);
1511 MarkMenuItem (char *menuRef, int state)
1513 MenuItem *item = MenuNameToItem(menuRef);
1515 if(item && item->handle) {
1516 ((GtkCheckMenuItem *) (item->handle))->active = state;
1522 EnableNamedMenuItem (char *menuRef, int state)
1524 MenuItem *item = MenuNameToItem(menuRef);
1526 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1531 EnableButtonBar (int state)
1534 XtSetSensitive(optList[W_BUTTON].handle, state);
1540 SetMenuEnables (Enables *enab)
1542 while (enab->name != NULL) {
1543 EnableNamedMenuItem(enab->name, enab->value);
1548 gboolean KeyPressProc(window, eventkey, data)
1550 GdkEventKey *eventkey;
1554 MoveTypeInProc(eventkey); // pop up for typed in moves
1557 /* check for other key values */
1558 switch(eventkey->keyval) {
1570 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1571 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1573 if(*nprms == 0) return;
1574 item = MenuNameToItem(prms[0]);
1575 if(item) ((MenuProc *) item->proc) ();
1589 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1590 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1591 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1592 dmEnables[i].piece);
1593 XtSetSensitive(entry, p != NULL || !appData.testLegality
1594 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1595 && !appData.icsActive));
1597 while (p && *p++ == dmEnables[i].piece) count++;
1598 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1600 XtSetArg(args[j], XtNlabel, label); j++;
1601 XtSetValues(entry, args, j);
1607 do_flash_delay (unsigned long msec)
1613 FlashDelay (int flash_delay)
1615 if(flash_delay) do_flash_delay(flash_delay);
1619 Fraction (int x, int start, int stop)
1621 double f = ((double) x - start)/(stop - start);
1622 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1626 static WindowPlacement wpNew;
1629 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1631 int touch=0, fudge = 4, f = 3;
1632 GetActualPlacement(sh, wp);
1633 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1634 if(abs(wp->x + wp->width + 2*frameX - f - wpMain.x) < fudge) touch = 2; else // left touch
1635 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1636 if(abs(wp->y + wp->height + frameX + frameY - f - wpMain.y) < fudge) touch = 4; // top touch
1637 //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);
1638 if(!touch ) return; // only windows that touch co-move
1639 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1640 int heightInc = wpNew.height - wpMain.height;
1641 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1642 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1643 wp->y += fracTop * heightInc;
1644 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1646 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1648 wp->height += heightInc;
1649 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1650 int widthInc = wpNew.width - wpMain.width;
1651 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1652 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1653 wp->y += fracLeft * widthInc;
1654 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1656 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1658 wp->width += widthInc;
1660 wp->x += wpNew.x - wpMain.x;
1661 wp->y += wpNew.y - wpMain.y;
1662 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1663 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1665 XtSetArg(args[j], XtNx, wp->x); j++;
1666 XtSetArg(args[j], XtNy, wp->y); j++;
1667 XtSetValues(sh, args, j);
1669 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1670 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1671 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1675 ReSize (WindowPlacement *wp)
1678 int sqx, sqy, i, w, h, lg = lineGap;
1679 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1680 gtk_widget_get_allocation(optList[W_DROP+1].handle, &a); // table that should contain everything
1681 w = a.width; h = a.height;
1682 gtk_widget_get_allocation(shellWidget, &a);
1683 if(a.width < w || a.height < h) { // outer window smaller than dialog content?
1684 w = a.width - w; h = a.height - h; // subtract matrgins, measured as table minus board dimensions
1685 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1686 w += a.width; h += a.height;
1688 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1689 w = a.width; h = a.height;
1691 sqx = (w - lg) / BOARD_WIDTH - lg;
1692 sqy = (h - lg) / BOARD_HEIGHT - lg;
1693 if(sqy < sqx) sqx = sqy;
1694 if(sqx < 20) return;
1695 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1697 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1698 sqx = (w - lg) / BOARD_WIDTH - lg;
1699 sqy = (h - lg) / BOARD_HEIGHT - lg;
1700 if(sqy < sqx) sqx = sqy;
1701 lg = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1702 if(sqx == oldSqx + 1 && lg == lineGap + 1) sqx = oldSqx, squareSize = 0; // prevent oscillations, force resize by kludge
1704 for(h=0; sizeDefaults[h+1].name && sizeDefaults[h].squareSize*8 > sqx*BOARD_WIDTH; h++) {}
1705 if(initialSquareSize != sizeDefaults[h].squareSize && !appData.fixedSize) { // boardSize changed
1706 initialSquareSize = sizeDefaults[h].squareSize; // used for saving font
1707 ChangeFont(1, &appData.clockFont, CLOCK_FONT, initialSquareSize, CLOCK_FONT_NAME, 2*(sizeDefaults[h].clockFontPxlSize+1)/3);
1708 ChangeFont(1, &appData.font, MESSAGE_FONT, initialSquareSize, DEFAULT_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1709 ChangeFont(0, &appData.icsFont, CONSOLE_FONT, initialSquareSize, CONSOLE_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1710 ChangeFont(0, &appData.tagsFont, EDITTAGS_FONT, initialSquareSize, TAGS_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1711 ChangeFont(0, &appData.commentFont, COMMENT_FONT, initialSquareSize, COMMENT_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1712 ChangeFont(0, &appData.gameListFont, GAMELIST_FONT, initialSquareSize, GAMELIST_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1713 ChangeFont(0, &appData.historyFont, MOVEHISTORY_FONT, initialSquareSize, HISTORY_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1714 DisplayBothClocks();
1715 ApplyFont(&mainOptions[W_MESSG], NULL);
1716 for(i=1; i<6; i++) ApplyFont(&mainOptions[W_BUTTON+i], NULL);
1717 ApplyFont(&tagsOptions[1], NULL);
1718 ApplyFont(&commentOptions[0], NULL);
1719 ApplyFont(&historyOptions[0], NULL);
1720 ApplyFont(&engoutOptions[5], NULL);
1721 ApplyFont(&engoutOptions[12], NULL);
1722 ApplyFont(&chatOptions[11], appData.icsFont);
1723 AppendColorized(&chatOptions[6], NULL, 0); // kludge to replace font tag
1725 if(!strchr(appData.boardSize, ',')) {
1726 ASSIGN(appData.boardSize, sizeDefaults[h].name);
1729 if(sizeDefaults[h].tinyLayout != tinyLayout) { // alter clipping of menu names to conform to board width
1730 int clip = (tinyLayout = sizeDefaults[h].tinyLayout) + 1;
1732 for(h=1; mainOptions[h].type == DropDown; h++) {
1733 strncpy(text, _(mainOptions[h].name), MSG_SIZ);
1734 if(clip != 1) text[clip + (text[clip-1] == '_')] = NULLCHAR;
1735 gtk_menu_item_set_label((GtkMenuItem *) mainOptions[h].handle, text);
1739 if(sqx != squareSize && !appData.fixedSize) {
1740 squareSize = sqx; // adopt new square size
1741 CreatePNGPieces(appData.pieceDirectory); // make newly scaled pieces
1742 InitDrawingSizes(0, 0); // creates grid etc.
1743 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1744 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1745 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1746 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1747 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1748 if(twoBoards && shellUp[DummyDlg]) {
1749 SlavePopUp(); dualOptions[3].max = 0; DoEvents(); // calls SlaveResize, kludge to force assigning new canvas
1750 partnerUp = !partnerUp; flipView = !flipView;
1751 DrawPosition(True, NULL);
1752 partnerUp = !partnerUp; flipView = !flipView;
1756 static guint delayedDragTag = 0;
1762 if(busy++) return; // prevent recursive calling, but remember we missed an event in 'busy'
1764 if(delayedDragTag) g_source_remove(delayedDragTag); // no more timer interrupts from same event!
1768 GetActualPlacement(shellWidget, &wpNew);
1769 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1770 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1771 busy = 0; break; // false alarm
1773 ReSize(&wpNew); // this can be interrupted by other events
1774 if(appData.useStickyWindows) {
1775 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1776 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1777 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1778 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1779 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1782 DrawPosition(True, NULL);
1783 if(busy > 2) busy = 2; // if multiple events were backlogged, only do one more
1790 //printf("old timr = %d\n", delayedDragTag);
1791 if(delayedDragTag) g_source_remove(delayedDragTag);
1792 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1793 //printf("new timr = %d\n", delayedDragTag);
1797 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1799 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1801 wpNew.x = event->configure.x;
1802 wpNew.y = event->configure.y;
1803 wpNew.width = event->configure.width;
1804 wpNew.height = event->configure.height;
1805 // SetWidgetLabel(&mainOptions[W_WHITE], ""); SetWidgetLabel(&mainOptions[W_BLACK], "");
1806 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1812 /* Disable all user input other than deleting the window */
1813 static int frozen = 0;
1819 /* Grab by a widget that doesn't accept input */
1820 gtk_grab_add(optList[W_MESSG].handle);
1824 /* Undo a FreezeUI */
1828 if (!frozen) return;
1829 gtk_grab_remove(optList[W_MESSG].handle);
1836 static int oldPausing = FALSE;
1837 static GameMode oldMode = (GameMode) -1;
1839 if (!boardWidget) return;
1841 if (pausing != oldPausing) {
1842 oldPausing = pausing;
1843 MarkMenuItem("Mode.Pause", pausing);
1845 if (appData.showButtonBar) {
1846 /* Always toggle, don't set. Previous code messes up when
1847 invoked while the button is pressed, as releasing it
1848 toggles the state again. */
1850 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1851 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1855 wname = ModeToWidgetName(oldMode);
1856 if (wname != NULL) {
1857 MarkMenuItem(wname, False);
1859 wname = ModeToWidgetName(gameMode);
1860 if (wname != NULL) {
1861 MarkMenuItem(wname, True);
1863 if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
1864 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1867 /* Maybe all the enables should be handled here, not just this one */
1868 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1870 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1875 * Button/menu procedures
1878 void CopyFileToClipboard(gchar *filename)
1880 gchar *selection_tmp;
1884 FILE* f = fopen(filename, "r");
1887 if (f == NULL) return;
1891 selection_tmp = g_try_malloc(len + 1);
1892 if (selection_tmp == NULL) {
1893 printf("Malloc failed in CopyFileToClipboard\n");
1896 count = fread(selection_tmp, 1, len, f);
1899 g_free(selection_tmp);
1902 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1904 // copy selection_tmp to clipboard
1905 GdkDisplay *gdisp = gdk_display_get_default();
1907 g_free(selection_tmp);
1910 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1911 gtk_clipboard_set_text(cb, selection_tmp, -1);
1912 g_free(selection_tmp);
1916 CopySomething (char *src)
1918 GdkDisplay *gdisp = gdk_display_get_default();
1920 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1921 if (gdisp == NULL) return;
1922 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1923 gtk_clipboard_set_text(cb, src, -1);
1927 PastePositionProc ()
1929 GdkDisplay *gdisp = gdk_display_get_default();
1933 if (gdisp == NULL) return;
1934 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1935 fenstr = gtk_clipboard_wait_for_text(cb);
1936 if (fenstr==NULL) return; // nothing had been selected to copy
1937 EditPositionPasteFEN(fenstr);
1946 guint len=0; int flip = appData.flipView;
1949 // get game from clipboard
1950 GdkDisplay *gdisp = gdk_display_get_default();
1951 if (gdisp == NULL) return;
1952 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1953 text = gtk_clipboard_wait_for_text(cb);
1954 if (text == NULL) return; // nothing to paste
1957 // write to temp file
1958 if (text == NULL || len == 0) {
1959 return; //nothing to paste
1961 f = fopen(gamePasteFilename, "w");
1963 DisplayError(_("Can't open temp file"), errno);
1966 fwrite(text, 1, len, f);
1970 if(!appData.autoFlipView) appData.flipView = flipView;
1971 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1972 appData.flipView = flip;
1979 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1985 void MoveTypeInProc(eventkey)
1986 GdkEventKey *eventkey;
1990 // ingnore if ctrl, alt, or meta is pressed
1991 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1995 buf[0]=eventkey->keyval;
1997 if (eventkey->keyval > 32 && eventkey->keyval < 256 || *buf == 27)
1998 ConsoleAutoPopUp (buf);
2003 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2005 if (!TempBackwardActive) {
2006 TempBackwardActive = True;
2012 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2014 /* Check to see if triggered by a key release event for a repeating key.
2015 * If so the next queued event will be a key press of the same key at the same time */
2016 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2018 XPeekEvent(xDisplay, &next);
2019 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2020 next.xkey.keycode == event->xkey.keycode)
2024 TempBackwardActive = False;
2030 { // called from menu
2033 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"man %s/../man/man6/xboard.6\"' -e 'end tell'", dataDir);
2036 system("xterm -e man xboard &");
2045 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);
2047 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
2055 SetWindowTitle (char *text, char *title, char *icon)
2060 if (appData.titleInWindow) {
2062 XtSetArg(args[i], XtNlabel, text); i++;
2063 XtSetValues(titleWidget, args, i);
2066 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2067 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2068 XtSetValues(shellWidget, args, i);
2069 XSync(xDisplay, False);
2071 if (appData.titleInWindow) {
2072 SetWidgetLabel(titleWidget, text);
2074 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2079 DisplayIcsInteractionTitle (String message)
2082 if (oldICSInteractionTitle == NULL) {
2083 /* Magic to find the old window title, adapted from vim */
2084 char *wina = getenv("WINDOWID");
2086 Window win = (Window) atoi(wina);
2087 Window root, parent, *children;
2088 unsigned int nchildren;
2089 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2091 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2092 if (!XQueryTree(xDisplay, win, &root, &parent,
2093 &children, &nchildren)) break;
2094 if (children) XFree((void *)children);
2095 if (parent == root || parent == 0) break;
2098 XSetErrorHandler(oldHandler);
2100 if (oldICSInteractionTitle == NULL) {
2101 oldICSInteractionTitle = "xterm";
2104 printf("\033]0;%s\007", message);
2110 LockBoardSize (int after)
2112 static char *oldClockFont, *oldMessgFont;
2114 if(oldMessgFont && !strcmp(oldMessgFont, appData.font) &&
2115 oldClockFont && !strcmp(oldClockFont, appData.clockFont) && after < 2) return; // only do something when font changed
2116 w = BOARD_WIDTH*(squareSize + lineGap) + lineGap;
2117 h = BOARD_HEIGHT*(squareSize + lineGap) + lineGap;
2119 ASSIGN(oldClockFont, appData.clockFont);
2120 ASSIGN(oldMessgFont, appData.font);
2121 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
2123 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board
2125 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
2130 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2132 static int twoLines = -1;
2133 GtkWidget *w = (GtkWidget *) opt->handle;
2135 char *markup, two = (appData.logoSize != 0);
2140 strcpy(bgcolor, "black");
2141 strcpy(fgcolor, "white");
2143 strcpy(bgcolor, "white");
2144 strcpy(fgcolor, "black");
2147 appData.lowTimeWarning &&
2148 (timer / 1000) < appData.icsAlarmTime) {
2149 strcpy(fgcolor, appData.lowTimeWarningColor);
2152 if(! partnerUp && two != twoLines) LockBoardSize(2); // lock board size if clock height changes
2154 gdk_color_parse( bgcolor, &col );
2155 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2157 if (appData.clockMode) {
2158 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2159 bgcolor, fgcolor, color, two ? "\n" : " ", TimeString(timer));
2160 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2161 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2163 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2164 bgcolor, fgcolor, color);
2165 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2166 // bgcolor, fgcolor, color);
2168 gtk_label_set_markup(GTK_LABEL(w), markup);
2171 if(!partnerUp && two != twoLines) LockBoardSize(3), twoLines = two;
2174 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2177 SetClockIcon (int color)
2179 GdkPixbuf *pm = *clockIcons[color];
2180 if (mainwindowIcon != pm) {
2181 mainwindowIcon = pm;
2183 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2185 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2190 #define INPUT_SOURCE_BUF_SIZE 8192
2199 char buf[INPUT_SOURCE_BUF_SIZE];
2204 DoInputCallback(io, cond, data)
2209 /* read input from one of the input source (for example a chess program, ICS, etc).
2210 * and call a function that will handle the input
2217 /* All information (callback function, file descriptor, etc) is
2218 * saved in an InputSource structure
2220 InputSource *is = (InputSource *) data;
2222 if (is->lineByLine) {
2223 count = read(is->fd, is->unused,
2224 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2226 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2227 RemoveInputSource(is); // cease reading stdin
2228 stdoutClosed = TRUE; // suppress future output
2231 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2234 is->unused += count;
2236 /* break input into lines and call the callback function on each
2239 while (p < is->unused) {
2240 q = memchr(p, '\n', is->unused - p);
2241 if (q == NULL) break;
2243 (is->func)(is, is->closure, p, q - p, 0);
2246 /* remember not yet used part of the buffer */
2248 while (p < is->unused) {
2253 /* read maximum length of input buffer and send the whole buffer
2254 * to the callback function
2256 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2261 (is->func)(is, is->closure, is->buf, count, error);
2263 return True; // Must return true or the watch will be removed
2266 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2273 GIOChannel *channel;
2274 ChildProc *cp = (ChildProc *) pr;
2276 is = (InputSource *) calloc(1, sizeof(InputSource));
2277 is->lineByLine = lineByLine;
2281 is->fd = fileno(stdin);
2283 is->kind = cp->kind;
2284 is->fd = cp->fdFrom;
2287 is->unused = is->buf;
2291 /* GTK-TODO: will this work on windows?*/
2293 channel = g_io_channel_unix_new(is->fd);
2294 g_io_channel_set_close_on_unref (channel, TRUE);
2295 is->sid = g_io_add_watch(channel, G_IO_IN|G_IO_HUP,(GIOFunc) DoInputCallback, is);
2297 is->closure = closure;
2298 return (InputSourceRef) is;
2303 RemoveInputSource(isr)
2306 InputSource *is = (InputSource *) isr;
2308 if (is->sid == 0) return;
2309 g_source_remove(is->sid);
2316 static Boolean frameWaiting;
2319 FrameAlarm (int sig)
2321 frameWaiting = False;
2322 /* In case System-V style signals. Needed?? */
2323 signal(SIGALRM, FrameAlarm);
2327 FrameDelay (int time)
2329 struct itimerval delay;
2332 frameWaiting = True;
2333 signal(SIGALRM, FrameAlarm);
2334 delay.it_interval.tv_sec =
2335 delay.it_value.tv_sec = time / 1000;
2336 delay.it_interval.tv_usec =
2337 delay.it_value.tv_usec = (time % 1000) * 1000;
2338 setitimer(ITIMER_REAL, &delay, NULL);
2339 while (frameWaiting) pause();
2340 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2341 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2342 setitimer(ITIMER_REAL, &delay, NULL);
2349 FrameDelay (int time)
2352 XSync(xDisplay, False);
2354 // gtk_main_iteration_do(False);
2357 usleep(time * 1000);
2363 FindLogo (char *place, char *name, char *buf)
2364 { // check if file exists in given place
2366 if(!place) return 0;
2367 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2368 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2376 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2378 char buf[MSG_SIZ], *logoName = buf;
2379 if(appData.logo[n][0]) {
2380 logoName = appData.logo[n];
2381 } else if(appData.autoLogo) {
2382 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2383 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2384 } else { // engine; cascade
2385 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2386 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2387 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2388 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2392 { ASSIGN(cps->programLogo, logoName); }
2396 UpdateLogos (int displ)
2398 if(optList[W_WHITE-1].handle == NULL) return;
2399 LoadLogo(&first, 0, 0);
2400 LoadLogo(&second, 1, appData.icsActive);
2401 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2405 void FileNamePopUpWrapper(
2416 GtkFileFilter *gtkfilter;
2417 GtkFileFilter *gtkfilter_all;
2419 char fileext[10] = "";
2420 char *result = NULL;
2422 char curDir[MSG_SIZ];
2424 StartDir(filter, NULL); // change to start directory for this file type
2426 if(def && *def && def[strlen(def)-1] == '/') {
2427 getcwd(curDir, MSG_SIZ);
2432 /* make a copy of the filter string, so that strtok can work with it*/
2433 cp = strdup(filter);
2435 /* add filters for file extensions */
2436 gtkfilter = gtk_file_filter_new();
2437 gtkfilter_all = gtk_file_filter_new();
2439 /* one filter to show everything */
2440 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2441 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2443 /* add filter if present */
2444 result = strtok(cp, space);
2445 while( result != NULL ) {
2446 snprintf(fileext,10,"*%s",result);
2447 result = strtok( NULL, space );
2448 gtk_file_filter_add_pattern(gtkfilter, fileext);
2451 /* second filter to only show what's useful */
2452 gtk_file_filter_set_name (gtkfilter,filter);
2454 if (openMode[0] == 'r')
2456 dialog = gtk_file_chooser_dialog_new (label,
2458 GTK_FILE_CHOOSER_ACTION_OPEN,
2459 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2460 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2465 dialog = gtk_file_chooser_dialog_new (label,
2467 GTK_FILE_CHOOSER_ACTION_SAVE,
2468 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2469 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2471 /* add filename suggestions */
2472 if (strlen(def) > 0 )
2473 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2475 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2479 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2480 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2481 /* activate filter */
2482 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2484 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2489 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2492 f = fopen(filename, openMode);
2495 DisplayError(_("Failed to open file"), errno);
2499 /* TODO add indec */
2501 ASSIGN(*name, filename);
2502 ScheduleDelayedEvent(DelayedLoad, 50);
2504 StartDir(filter, filename);
2507 else StartDir(filter, "");
2509 gtk_widget_destroy (dialog);
2512 if(def && *def && def[strlen(def)-1] == '/') chdir(curDir);