2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
9 * Software Foundation, Inc.
11 * The following terms apply to Digital Equipment Corporation's copyright
13 * ------------------------------------------------------------------------
16 * Permission to use, copy, modify, and distribute this software and its
17 * documentation for any purpose and without fee is hereby granted,
18 * provided that the above copyright notice appear in all copies and that
19 * both that copyright notice and this permission notice appear in
20 * supporting documentation, and that the name of Digital not be
21 * used in advertising or publicity pertaining to distribution of the
22 * software without specific, written prior permission.
24 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
26 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
27 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
28 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
29 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
31 * ------------------------------------------------------------------------
33 * The following terms apply to the enhanced version of XBoard
34 * distributed by the Free Software Foundation:
35 * ------------------------------------------------------------------------
37 * GNU XBoard is free software: you can redistribute it and/or modify
38 * it under the terms of the GNU General Public License as published by
39 * the Free Software Foundation, either version 3 of the License, or (at
40 * your option) any later version.
42 * GNU XBoard is distributed in the hope that it will be useful, but
43 * WITHOUT ANY WARRANTY; without even the implied warranty of
44 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
45 * General Public License for more details.
47 * You should have received a copy of the GNU General Public License
48 * along with this program. If not, see http://www.gnu.org/licenses/. *
50 *------------------------------------------------------------------------
51 ** See the file ChangeLog for a revision history. */
61 #include <sys/types.h>
65 #include <cairo/cairo.h>
69 # if HAVE_SYS_SOCKET_H
70 # include <sys/socket.h>
71 # include <netinet/in.h>
73 # else /* not HAVE_SYS_SOCKET_H */
74 # if HAVE_LAN_SOCKET_H
75 # include <lan/socket.h>
77 # include <lan/netdb.h>
78 # else /* not HAVE_LAN_SOCKET_H */
79 # define OMIT_SOCKETS 1
80 # endif /* not HAVE_LAN_SOCKET_H */
81 # endif /* not HAVE_SYS_SOCKET_H */
82 #endif /* !OMIT_SOCKETS */
87 #else /* not STDC_HEADERS */
88 extern char *getenv();
91 # else /* not HAVE_STRING_H */
93 # endif /* not HAVE_STRING_H */
94 #endif /* not STDC_HEADERS */
97 # include <sys/fcntl.h>
98 #else /* not HAVE_SYS_FCNTL_H */
101 # endif /* HAVE_FCNTL_H */
102 #endif /* not HAVE_SYS_FCNTL_H */
104 #if HAVE_SYS_SYSTEMINFO_H
105 # include <sys/systeminfo.h>
106 #endif /* HAVE_SYS_SYSTEMINFO_H */
108 #if TIME_WITH_SYS_TIME
109 # include <sys/time.h>
113 # include <sys/time.h>
124 # include <sys/wait.h>
129 # define NAMLEN(dirent) strlen((dirent)->d_name)
130 # define HAVE_DIR_STRUCT
132 # define dirent direct
133 # define NAMLEN(dirent) (dirent)->d_namlen
135 # include <sys/ndir.h>
136 # define HAVE_DIR_STRUCT
139 # include <sys/dir.h>
140 # define HAVE_DIR_STRUCT
144 # define HAVE_DIR_STRUCT
152 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
155 #include "frontend.h"
157 #include "backendz.h"
165 #include "engineoutput.h"
171 # include <gtkmacintegration/gtkosxapplication.h>
172 // prevent pathname of positional file argument provided by OS X being be mistaken for option name
173 // (price is that we won't recognize Windows option format anymore).
176 // redefine some defaults
179 # undef SETTINGS_FILE
180 # define ICS_LOGON "Library/Preferences/XboardICS.conf"
181 # define LOCALEDIR localeDir
182 # define SETTINGS_FILE masterSettings
183 # define SYNC_MENUBAR gtkosx_application_sync_menubar(theApp)
184 char localeDir[MSG_SIZ];
185 char masterSettings[MSG_SIZ];
189 # define SYNC_MENUBAR
196 #define usleep(t) _sleep2(((t)+500)/1000)
200 # define _(s) gettext (s)
201 # define N_(s) gettext_noop (s)
207 int main P((int argc, char **argv));
208 RETSIGTYPE CmailSigHandler P((int sig));
209 RETSIGTYPE IntSigHandler P((int sig));
210 RETSIGTYPE TermSizeSigHandler P((int sig));
211 char *InsertPxlSize P((char *pattern, int targetPxlSize));
214 XFontSet CreateFontSet P((char *base_fnt_lst));
216 char *FindFont P((char *pattern, int targetPxlSize));
219 void DelayedDrag P((void));
220 void ICSInputBoxPopUp P((void));
221 void MoveTypeInProc P((GdkEventKey *eventkey));
222 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
223 Boolean TempBackwardActive = False;
224 void DisplayMove P((int moveNumber));
225 void update_ics_width P(());
226 int CopyMemoProc P(());
227 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
228 static int FindLogo P((char *place, char *name, char *buf));
232 XFontSet fontSet, clockFontSet;
235 XFontStruct *clockFontStruct;
237 Font coordFontID, countFontID;
238 XFontStruct *coordFontStruct, *countFontStruct;
240 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
241 GtkWidget *mainwindow;
243 Option *optList; // contains all widgets of main window
246 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
249 static GdkPixbuf *mainwindowIcon=NULL;
250 static GdkPixbuf *WhiteIcon=NULL;
251 static GdkPixbuf *BlackIcon=NULL;
253 /* key board accelerators */
254 GtkAccelGroup *GtkAccelerators;
256 typedef unsigned int BoardSize;
258 Boolean chessProgram;
259 static int initialSquareSize;
261 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
262 int smallLayout = 0, tinyLayout = 0,
263 marginW, marginH, // [HGM] for run-time resizing
264 fromX = -1, fromY = -1, toX, toY, commentUp = False,
265 errorExitStatus = -1, defaultLineGap;
267 Dimension textHeight;
269 char *chessDir, *programName, *programVersion;
270 Boolean alwaysOnTop = False;
271 char *icsTextMenuString;
273 char *firstChessProgramNames;
274 char *secondChessProgramNames;
276 WindowPlacement wpMain;
277 WindowPlacement wpConsole;
278 WindowPlacement wpComment;
279 WindowPlacement wpMoveHistory;
280 WindowPlacement wpEvalGraph;
281 WindowPlacement wpEngineOutput;
282 WindowPlacement wpGameList;
283 WindowPlacement wpTags;
284 WindowPlacement wpDualBoard;
286 /* This magic number is the number of intermediate frames used
287 in each half of the animation. For short moves it's reduced
288 by 1. The total number of frames will be factor * 2 + 1. */
291 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
298 DropMenuEnables dmEnables[] = {
307 XtResource clientResources[] = {
308 { "flashCount", "flashCount", XtRInt, sizeof(int),
309 XtOffset(AppDataPtr, flashCount), XtRImmediate,
310 (XtPointer) FLASH_COUNT },
314 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
315 char globalTranslations[] =
316 ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
317 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
318 :<KeyDown>Return: TempBackwardProc() \n \
319 :<KeyUp>Return: TempForwardProc() \n";
321 char ICSInputTranslations[] =
322 "<Key>Up: UpKeyProc() \n "
323 "<Key>Down: DownKeyProc() \n "
324 "<Key>Return: EnterKeyProc() \n";
326 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
327 // as the widget is destroyed before the up-click can call extend-end
328 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
331 String xboardResources[] = {
332 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
340 gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
343 //---------------------------------------------------------------------------------------------------------
344 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
347 #define CW_USEDEFAULT (1<<31)
348 #define ICS_TEXT_MENU_SIZE 90
349 #define DEBUG_FILE "xboard.debug"
350 #define SetCurrentDirectory chdir
351 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
355 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
358 // front-end part of option handling
360 // [HGM] This platform-dependent table provides the location for storing the color info
361 extern char *crWhite, * crBlack;
365 &appData.whitePieceColor,
366 &appData.blackPieceColor,
367 &appData.lightSquareColor,
368 &appData.darkSquareColor,
369 &appData.highlightSquareColor,
370 &appData.premoveHighlightColor,
371 &appData.lowTimeWarningColor,
382 // [HGM] font: keep a font for each square size, even non-stndard ones
385 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
386 char *fontTable[NUM_FONTS][MAX_SIZE];
389 ParseFont (char *name, int number)
390 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
392 if(sscanf(name, "size%d:", &size)) {
393 // [HGM] font: font is meant for specific boardSize (likely from settings file);
394 // defer processing it until we know if it matches our board size
395 if(!strstr(name, "-*-") && // ignore X-fonts
396 size >= 0 && size<MAX_SIZE) { // for now, fixed limit
397 fontTable[number][size] = strdup(strchr(name, ':')+1);
398 fontValid[number][size] = True;
403 case 0: // CLOCK_FONT
404 appData.clockFont = strdup(name);
406 case 1: // MESSAGE_FONT
407 appData.font = strdup(name);
409 case 2: // COORD_FONT
410 appData.coordFont = strdup(name);
413 appData.icsFont = strdup(name);
416 appData.tagsFont = strdup(name);
419 appData.commentFont = strdup(name);
421 case MOVEHISTORY_FONT:
422 appData.historyFont = strdup(name);
425 appData.gameListFont = strdup(name);
430 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
435 { // only 2 fonts currently
436 appData.clockFont = strdup(CLOCK_FONT_NAME);
437 appData.coordFont = strdup(COORD_FONT_NAME);
438 appData.font = strdup(DEFAULT_FONT_NAME);
439 appData.icsFont = strdup(CONSOLE_FONT_NAME);
440 appData.tagsFont = strdup(TAGS_FONT_NAME);
441 appData.commentFont = strdup(COMMENT_FONT_NAME);
442 appData.historyFont = strdup(HISTORY_FONT_NAME);
443 appData.gameListFont = strdup(GAMELIST_FONT_NAME);
447 ChangeFont (int force, char **font, int fnr, int size, char *def, int pix)
449 if(!fontValid[fnr][size]) {
450 if(fontIsSet[fnr] && !force) return; // unless forced we do not replace an explicitly specified font by a default
451 ASSIGN(fontTable[fnr][size], def); // use default
452 fontIsSet[fnr] = False;
454 FREE(*font); *font = InsertPxlSize(fontTable[fnr][size], pix);
459 { // no-op, until we identify the code for this already in XBoard and move it here
463 ParseColor (int n, char *name)
464 { // in XBoard, just copy the color-name string
465 if(colorVariable[n] && *name == '#') *(char**)colorVariable[n] = strdup(name);
471 return *(char**)colorVariable[n];
475 ParseTextAttribs (ColorClass cc, char *s)
477 (&appData.colorShout)[cc] = strdup(s);
481 ParseBoardSize (void *addr, char *name)
483 appData.boardSize = strdup(name);
488 { // In XBoard the sound-playing program takes care of obtaining the actual sound
492 SetCommPortDefaults ()
493 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
496 // [HGM] args: these three cases taken out to stay in front-end
498 SaveFontArg (FILE *f, ArgDescriptor *ad)
501 int i, n = (int)(intptr_t)ad->argLoc;
503 case 0: // CLOCK_FONT
504 name = appData.clockFont;
506 case 1: // MESSAGE_FONT
509 case 2: // COORD_FONT
510 name = appData.coordFont;
513 name = appData.icsFont;
516 name = appData.tagsFont;
519 name = appData.commentFont;
521 case MOVEHISTORY_FONT:
522 name = appData.historyFont;
525 name = appData.gameListFont;
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 fontTable[n][initialSquareSize] = strdup(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 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // 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;
1156 formWidget = XtParent(boardWidget);
1157 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1158 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1159 XtGetValues(optList[W_WHITE].handle, args, 2);
1160 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1161 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1162 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1163 XtGetValues(optList[W_PAUSE].handle, args, 2);
1167 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1168 // not need to go into InitDrawingSizes().
1172 // add accelerators to main shell
1173 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1176 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1178 WhiteIcon = LoadIconFile("icon_white");
1179 BlackIcon = LoadIconFile("icon_black");
1180 SetClockIcon(0); // sets white icon
1184 * Create a cursor for the board widget.
1187 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1188 XChangeWindowAttributes(xDisplay, xBoardWindow,
1189 CWCursor, &window_attributes);
1193 * Inhibit shell resizing.
1196 shellArgs[0].value = (XtArgVal) &w;
1197 shellArgs[1].value = (XtArgVal) &h;
1198 XtGetValues(shellWidget, shellArgs, 2);
1199 shellArgs[4].value = shellArgs[2].value = w;
1200 shellArgs[5].value = shellArgs[3].value = h;
1201 // XtSetValues(shellWidget, &shellArgs[2], 4);
1204 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1205 // It wil only become known asynchronously, when we first write a string into it.
1206 // This will then change the clock widget height, which triggers resizing the top-level window
1207 // and a configure event. Only then can we know the total height of the top-level window,
1208 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1209 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1212 gtk_widget_get_allocation(shells[BoardWindow], &a);
1213 w = a.width; h = a.height;
1214 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1215 clockKludge = hc = a.height;
1216 gtk_widget_get_allocation(boardWidget, &a);
1217 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1218 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1224 if(appData.logoSize)
1225 { // locate and read user logo
1226 char buf[MSG_SIZ], name[MSG_SIZ];
1227 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1228 if(!FindLogo(name, ".logo", buf))
1229 FindLogo(appData.logoDir, name + 6, buf);
1230 ASSIGN(userLogo, buf);
1233 if (appData.animate || appData.animateDragging)
1236 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1237 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1239 /* [AS] Restore layout */
1240 if( wpMoveHistory.visible ) {
1244 if( wpEvalGraph.visible )
1249 if( wpEngineOutput.visible ) {
1250 EngineOutputPopUp();
1253 if( wpConsole.visible && appData.icsActive ) {
1258 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1263 if (errorExitStatus == -1) {
1264 if (appData.icsActive) {
1265 /* We now wait until we see "login:" from the ICS before
1266 sending the logon script (problems with timestamp otherwise) */
1267 /*ICSInitScript();*/
1268 if (appData.icsInputBox) ICSInputBoxPopUp();
1272 signal(SIGWINCH, TermSizeSigHandler);
1274 signal(SIGINT, IntSigHandler);
1275 signal(SIGTERM, IntSigHandler);
1276 if (*appData.cmailGameName != NULLCHAR) {
1277 signal(SIGUSR1, CmailSigHandler);
1282 // XtSetKeyboardFocus(shellWidget, formWidget);
1284 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1287 /* check for GTK events and process them */
1290 gtk_main_iteration();
1293 if (appData.debugMode) fclose(debugFP); // [DM] debug
1300 while(gtk_events_pending()) gtk_main_iteration();
1304 TermSizeSigHandler (int sig)
1310 IntSigHandler (int sig)
1316 CmailSigHandler (int sig)
1321 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1323 /* Activate call-back function CmailSigHandlerCallBack() */
1324 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1326 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1330 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1333 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1335 /**** end signal code ****/
1338 #define Abs(n) ((n)<0 ? -(n) : (n))
1341 InsertPxlSize (char *pattern, int targetPxlSize)
1344 snprintf(buf, MSG_SIZ, pattern, targetPxlSize); // pattern is something like "Sans Bold %d"
1351 InsertPxlSize (char *pattern, int targetPxlSize)
1353 char *base_fnt_lst, strInt[12], *p, *q;
1354 int alternatives, i, len, strIntLen;
1357 * Replace the "*" (if present) in the pixel-size slot of each
1358 * alternative with the targetPxlSize.
1362 while ((p = strchr(p, ',')) != NULL) {
1366 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1367 strIntLen = strlen(strInt);
1368 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1372 while (alternatives--) {
1373 char *comma = strchr(p, ',');
1374 for (i=0; i<14; i++) {
1375 char *hyphen = strchr(p, '-');
1377 if (comma && hyphen > comma) break;
1378 len = hyphen + 1 - p;
1379 if (i == 7 && *p == '*' && len == 2) {
1381 memcpy(q, strInt, strIntLen);
1391 len = comma + 1 - p;
1398 return base_fnt_lst;
1404 CreateFontSet (char *base_fnt_lst)
1407 char **missing_list;
1411 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1412 &missing_list, &missing_count, &def_string);
1413 if (appData.debugMode) {
1415 XFontStruct **font_struct_list;
1416 char **font_name_list;
1417 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1419 fprintf(debugFP, " got list %s, locale %s\n",
1420 XBaseFontNameListOfFontSet(fntSet),
1421 XLocaleOfFontSet(fntSet));
1422 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1423 for (i = 0; i < count; i++) {
1424 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1427 for (i = 0; i < missing_count; i++) {
1428 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1431 if (fntSet == NULL) {
1432 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1438 #else // not ENABLE_NLS
1440 * Find a font that matches "pattern" that is as close as
1441 * possible to the targetPxlSize. Prefer fonts that are k
1442 * pixels smaller to fonts that are k pixels larger. The
1443 * pattern must be in the X Consortium standard format,
1444 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1445 * The return value should be freed with XtFree when no
1450 FindFont (char *pattern, int targetPxlSize)
1452 char **fonts, *p, *best, *scalable, *scalableTail;
1453 int i, j, nfonts, minerr, err, pxlSize;
1455 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1457 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1458 programName, pattern);
1465 for (i=0; i<nfonts; i++) {
1468 if (*p != '-') continue;
1470 if (*p == NULLCHAR) break;
1471 if (*p++ == '-') j++;
1473 if (j < 7) continue;
1476 scalable = fonts[i];
1479 err = pxlSize - targetPxlSize;
1480 if (Abs(err) < Abs(minerr) ||
1481 (minerr > 0 && err < 0 && -err == minerr)) {
1487 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1488 /* If the error is too big and there is a scalable font,
1489 use the scalable font. */
1490 int headlen = scalableTail - scalable;
1491 p = (char *) XtMalloc(strlen(scalable) + 10);
1492 while (isdigit(*scalableTail)) scalableTail++;
1493 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1495 p = (char *) XtMalloc(strlen(best) + 2);
1496 safeStrCpy(p, best, strlen(best)+1 );
1498 if (appData.debugMode) {
1499 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1500 pattern, targetPxlSize, p);
1502 XFreeFontNames(fonts);
1509 MarkMenuItem (char *menuRef, int state)
1511 MenuItem *item = MenuNameToItem(menuRef);
1513 if(item && item->handle) {
1514 ((GtkCheckMenuItem *) (item->handle))->active = state;
1520 EnableNamedMenuItem (char *menuRef, int state)
1522 MenuItem *item = MenuNameToItem(menuRef);
1524 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1529 EnableButtonBar (int state)
1532 XtSetSensitive(optList[W_BUTTON].handle, state);
1538 SetMenuEnables (Enables *enab)
1540 while (enab->name != NULL) {
1541 EnableNamedMenuItem(enab->name, enab->value);
1546 gboolean KeyPressProc(window, eventkey, data)
1548 GdkEventKey *eventkey;
1552 MoveTypeInProc(eventkey); // pop up for typed in moves
1555 /* check for other key values */
1556 switch(eventkey->keyval) {
1568 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1569 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1571 if(*nprms == 0) return;
1572 item = MenuNameToItem(prms[0]);
1573 if(item) ((MenuProc *) item->proc) ();
1587 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1588 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1589 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1590 dmEnables[i].piece);
1591 XtSetSensitive(entry, p != NULL || !appData.testLegality
1592 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1593 && !appData.icsActive));
1595 while (p && *p++ == dmEnables[i].piece) count++;
1596 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1598 XtSetArg(args[j], XtNlabel, label); j++;
1599 XtSetValues(entry, args, j);
1605 do_flash_delay (unsigned long msec)
1611 FlashDelay (int flash_delay)
1613 if(flash_delay) do_flash_delay(flash_delay);
1617 Fraction (int x, int start, int stop)
1619 double f = ((double) x - start)/(stop - start);
1620 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1624 static WindowPlacement wpNew;
1627 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1629 int touch=0, fudge = 4, f = 3;
1630 GetActualPlacement(sh, wp);
1631 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1632 if(abs(wp->x + wp->width + 2*frameX - f - wpMain.x) < fudge) touch = 2; else // left touch
1633 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1634 if(abs(wp->y + wp->height + frameX + frameY - f - wpMain.y) < fudge) touch = 4; // top touch
1635 //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);
1636 if(!touch ) return; // only windows that touch co-move
1637 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1638 int heightInc = wpNew.height - wpMain.height;
1639 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1640 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1641 wp->y += fracTop * heightInc;
1642 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1644 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1646 wp->height += heightInc;
1647 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1648 int widthInc = wpNew.width - wpMain.width;
1649 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1650 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1651 wp->y += fracLeft * widthInc;
1652 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1654 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1656 wp->width += widthInc;
1658 wp->x += wpNew.x - wpMain.x;
1659 wp->y += wpNew.y - wpMain.y;
1660 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1661 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1663 XtSetArg(args[j], XtNx, wp->x); j++;
1664 XtSetArg(args[j], XtNy, wp->y); j++;
1665 XtSetValues(sh, args, j);
1667 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1668 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1669 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1673 ReSize (WindowPlacement *wp)
1676 int sqx, sqy, i, w, h, lg = lineGap;
1677 static int first = 1;
1678 // DisplayBothClocks();
1679 if(wp->width == wpMain.width && wp->height == wpMain.height && !first) 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].name && sizeDefaults[h].squareSize*8 > sqx*BOARD_WIDTH; h++) {}
1705 if(initialSquareSize != sizeDefaults[h].squareSize) { // 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.coordFont, 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);
1718 if(!strchr(appData.boardSize, ',')) {
1719 ASSIGN(appData.boardSize, sizeDefaults[h].name);
1722 if(sizeDefaults[h].tinyLayout != tinyLayout) { // alter clipping of menu names to conform to board width
1723 int clip = (tinyLayout = sizeDefaults[h].tinyLayout) + 1;
1725 for(h=1; mainOptions[h].type == DropDown; h++) {
1726 strncpy(text, _(mainOptions[h].name), MSG_SIZ);
1727 if(clip != 1) text[clip + (text[clip-1] == '_')] = NULLCHAR;
1728 gtk_menu_item_set_label((GtkMenuItem *) mainOptions[h].handle, text);
1732 if(sqx != squareSize && !first) {
1733 squareSize = sqx; // adopt new square size
1734 CreatePNGPieces(); // make newly scaled pieces
1735 InitDrawingSizes(0, 0); // creates grid etc.
1736 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1737 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1738 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1739 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1740 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1741 first = appData.fixedSize;
1742 if(twoBoards && shellUp[DummyDlg]) {
1743 SlavePopUp(); dualOptions[3].max = 0; DoEvents(); // calls SlaveResize, kludge to force assigning new canvas
1744 partnerUp = !partnerUp; flipView = !flipView;
1745 DrawPosition(True, NULL);
1746 partnerUp = !partnerUp; flipView = !flipView;
1750 static guint delayedDragTag = 0;
1756 if(busy) { // prevent recursive calling, but postpone interrupting call rather than lose it
1757 if(!delayedDragTag) delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1761 GetActualPlacement(shellWidget, &wpNew);
1762 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1763 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1764 busy = 0; return; // false alarm
1767 if(appData.useStickyWindows) {
1768 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1769 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1770 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1771 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1772 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1775 DrawPosition(True, NULL);
1776 if(delayedDragTag) g_source_remove(delayedDragTag);
1777 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1784 //printf("old timr = %d\n", delayedDragTag);
1785 if(delayedDragTag) g_source_remove(delayedDragTag);
1786 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1787 //printf("new timr = %d\n", delayedDragTag);
1791 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1793 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1795 wpNew.x = event->configure.x;
1796 wpNew.y = event->configure.y;
1797 wpNew.width = event->configure.width;
1798 wpNew.height = event->configure.height;
1799 // SetWidgetLabel(&mainOptions[W_WHITE], ""); SetWidgetLabel(&mainOptions[W_BLACK], "");
1800 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1806 /* Disable all user input other than deleting the window */
1807 static int frozen = 0;
1813 /* Grab by a widget that doesn't accept input */
1814 gtk_grab_add(optList[W_MESSG].handle);
1818 /* Undo a FreezeUI */
1822 if (!frozen) return;
1823 gtk_grab_remove(optList[W_MESSG].handle);
1830 static int oldPausing = FALSE;
1831 static GameMode oldMode = (GameMode) -1;
1833 if (!boardWidget) return;
1835 if (pausing != oldPausing) {
1836 oldPausing = pausing;
1837 MarkMenuItem("Mode.Pause", pausing);
1839 if (appData.showButtonBar) {
1840 /* Always toggle, don't set. Previous code messes up when
1841 invoked while the button is pressed, as releasing it
1842 toggles the state again. */
1844 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1845 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1849 wname = ModeToWidgetName(oldMode);
1850 if (wname != NULL) {
1851 MarkMenuItem(wname, False);
1853 wname = ModeToWidgetName(gameMode);
1854 if (wname != NULL) {
1855 MarkMenuItem(wname, True);
1857 if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
1858 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1861 /* Maybe all the enables should be handled here, not just this one */
1862 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1864 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1869 * Button/menu procedures
1872 void CopyFileToClipboard(gchar *filename)
1874 gchar *selection_tmp;
1878 FILE* f = fopen(filename, "r");
1881 if (f == NULL) return;
1885 selection_tmp = g_try_malloc(len + 1);
1886 if (selection_tmp == NULL) {
1887 printf("Malloc failed in CopyFileToClipboard\n");
1890 count = fread(selection_tmp, 1, len, f);
1893 g_free(selection_tmp);
1896 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1898 // copy selection_tmp to clipboard
1899 GdkDisplay *gdisp = gdk_display_get_default();
1901 g_free(selection_tmp);
1904 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1905 gtk_clipboard_set_text(cb, selection_tmp, -1);
1906 g_free(selection_tmp);
1910 CopySomething (char *src)
1912 GdkDisplay *gdisp = gdk_display_get_default();
1914 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1915 if (gdisp == NULL) return;
1916 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1917 gtk_clipboard_set_text(cb, src, -1);
1921 PastePositionProc ()
1923 GdkDisplay *gdisp = gdk_display_get_default();
1927 if (gdisp == NULL) return;
1928 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1929 fenstr = gtk_clipboard_wait_for_text(cb);
1930 if (fenstr==NULL) return; // nothing had been selected to copy
1931 EditPositionPasteFEN(fenstr);
1940 guint len=0; int flip = appData.flipView;
1943 // get game from clipboard
1944 GdkDisplay *gdisp = gdk_display_get_default();
1945 if (gdisp == NULL) return;
1946 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1947 text = gtk_clipboard_wait_for_text(cb);
1948 if (text == NULL) return; // nothing to paste
1951 // write to temp file
1952 if (text == NULL || len == 0) {
1953 return; //nothing to paste
1955 f = fopen(gamePasteFilename, "w");
1957 DisplayError(_("Can't open temp file"), errno);
1960 fwrite(text, 1, len, f);
1964 if(!appData.autoFlipView) appData.flipView = flipView;
1965 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1966 appData.flipView = flip;
1973 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1979 void MoveTypeInProc(eventkey)
1980 GdkEventKey *eventkey;
1984 // ingnore if ctrl, alt, or meta is pressed
1985 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1989 buf[0]=eventkey->keyval;
1991 if (eventkey->keyval > 32 && eventkey->keyval < 256 || *buf == 27)
1992 ConsoleAutoPopUp (buf);
1997 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1999 if (!TempBackwardActive) {
2000 TempBackwardActive = True;
2006 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2008 /* Check to see if triggered by a key release event for a repeating key.
2009 * If so the next queued event will be a key press of the same key at the same time */
2010 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2012 XPeekEvent(xDisplay, &next);
2013 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2014 next.xkey.keycode == event->xkey.keycode)
2018 TempBackwardActive = False;
2024 { // called from menu
2027 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"man %s/../man/man6/xboard.6\"' -e 'end tell'", dataDir);
2030 system("xterm -e man xboard &");
2039 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);
2041 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
2049 SetWindowTitle (char *text, char *title, char *icon)
2054 if (appData.titleInWindow) {
2056 XtSetArg(args[i], XtNlabel, text); i++;
2057 XtSetValues(titleWidget, args, i);
2060 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2061 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2062 XtSetValues(shellWidget, args, i);
2063 XSync(xDisplay, False);
2065 if (appData.titleInWindow) {
2066 SetWidgetLabel(titleWidget, text);
2068 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2073 DisplayIcsInteractionTitle (String message)
2076 if (oldICSInteractionTitle == NULL) {
2077 /* Magic to find the old window title, adapted from vim */
2078 char *wina = getenv("WINDOWID");
2080 Window win = (Window) atoi(wina);
2081 Window root, parent, *children;
2082 unsigned int nchildren;
2083 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2085 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2086 if (!XQueryTree(xDisplay, win, &root, &parent,
2087 &children, &nchildren)) break;
2088 if (children) XFree((void *)children);
2089 if (parent == root || parent == 0) break;
2092 XSetErrorHandler(oldHandler);
2094 if (oldICSInteractionTitle == NULL) {
2095 oldICSInteractionTitle = "xterm";
2098 printf("\033]0;%s\007", message);
2104 LockBoardSize (int after)
2106 static char *oldClockFont, *oldMessgFont;
2108 if(oldMessgFont && !strcmp(oldMessgFont, appData.font) &&
2109 oldClockFont && !strcmp(oldClockFont, appData.clockFont) && after < 2) return; // only do something when font changed
2110 w = BOARD_WIDTH*(squareSize + lineGap) + lineGap;
2111 h = BOARD_HEIGHT*(squareSize + lineGap) + lineGap;
2113 ASSIGN(oldClockFont, appData.clockFont);
2114 ASSIGN(oldMessgFont, appData.font);
2115 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
2117 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board
2119 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
2124 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2126 static int twoLines = -1;
2127 GtkWidget *w = (GtkWidget *) opt->handle;
2129 char *markup, two = (appData.logoSize != 0);
2134 strcpy(bgcolor, "black");
2135 strcpy(fgcolor, "white");
2137 strcpy(bgcolor, "white");
2138 strcpy(fgcolor, "black");
2141 appData.lowTimeWarning &&
2142 (timer / 1000) < appData.icsAlarmTime) {
2143 strcpy(fgcolor, appData.lowTimeWarningColor);
2146 if(! partnerUp && two != twoLines) LockBoardSize(2); // lock board size if clock height changes
2148 gdk_color_parse( bgcolor, &col );
2149 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2151 if (appData.clockMode) {
2152 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2153 bgcolor, fgcolor, color, two ? "\n" : " ", TimeString(timer));
2154 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2155 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2157 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2158 bgcolor, fgcolor, color);
2159 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2160 // bgcolor, fgcolor, color);
2162 gtk_label_set_markup(GTK_LABEL(w), markup);
2165 if(!partnerUp && two != twoLines) LockBoardSize(3), twoLines = two;
2168 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2171 SetClockIcon (int color)
2173 GdkPixbuf *pm = *clockIcons[color];
2174 if (mainwindowIcon != pm) {
2175 mainwindowIcon = pm;
2177 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2179 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2184 #define INPUT_SOURCE_BUF_SIZE 8192
2193 char buf[INPUT_SOURCE_BUF_SIZE];
2198 DoInputCallback(io, cond, data)
2203 /* read input from one of the input source (for example a chess program, ICS, etc).
2204 * and call a function that will handle the input
2211 /* All information (callback function, file descriptor, etc) is
2212 * saved in an InputSource structure
2214 InputSource *is = (InputSource *) data;
2216 if (is->lineByLine) {
2217 count = read(is->fd, is->unused,
2218 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2220 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2221 RemoveInputSource(is); // cease reading stdin
2222 stdoutClosed = TRUE; // suppress future output
2225 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2228 is->unused += count;
2230 /* break input into lines and call the callback function on each
2233 while (p < is->unused) {
2234 q = memchr(p, '\n', is->unused - p);
2235 if (q == NULL) break;
2237 (is->func)(is, is->closure, p, q - p, 0);
2240 /* remember not yet used part of the buffer */
2242 while (p < is->unused) {
2247 /* read maximum length of input buffer and send the whole buffer
2248 * to the callback function
2250 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2255 (is->func)(is, is->closure, is->buf, count, error);
2257 return True; // Must return true or the watch will be removed
2260 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2267 GIOChannel *channel;
2268 ChildProc *cp = (ChildProc *) pr;
2270 is = (InputSource *) calloc(1, sizeof(InputSource));
2271 is->lineByLine = lineByLine;
2275 is->fd = fileno(stdin);
2277 is->kind = cp->kind;
2278 is->fd = cp->fdFrom;
2281 is->unused = is->buf;
2285 /* GTK-TODO: will this work on windows?*/
2287 channel = g_io_channel_unix_new(is->fd);
2288 g_io_channel_set_close_on_unref (channel, TRUE);
2289 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2291 is->closure = closure;
2292 return (InputSourceRef) is;
2297 RemoveInputSource(isr)
2300 InputSource *is = (InputSource *) isr;
2302 if (is->sid == 0) return;
2303 g_source_remove(is->sid);
2310 static Boolean frameWaiting;
2313 FrameAlarm (int sig)
2315 frameWaiting = False;
2316 /* In case System-V style signals. Needed?? */
2317 signal(SIGALRM, FrameAlarm);
2321 FrameDelay (int time)
2323 struct itimerval delay;
2326 frameWaiting = True;
2327 signal(SIGALRM, FrameAlarm);
2328 delay.it_interval.tv_sec =
2329 delay.it_value.tv_sec = time / 1000;
2330 delay.it_interval.tv_usec =
2331 delay.it_value.tv_usec = (time % 1000) * 1000;
2332 setitimer(ITIMER_REAL, &delay, NULL);
2333 while (frameWaiting) pause();
2334 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2335 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2336 setitimer(ITIMER_REAL, &delay, NULL);
2343 FrameDelay (int time)
2346 XSync(xDisplay, False);
2348 // gtk_main_iteration_do(False);
2351 usleep(time * 1000);
2357 FindLogo (char *place, char *name, char *buf)
2358 { // check if file exists in given place
2360 if(!place) return 0;
2361 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2362 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2370 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2372 char buf[MSG_SIZ], *logoName = buf;
2373 if(appData.logo[n][0]) {
2374 logoName = appData.logo[n];
2375 } else if(appData.autoLogo) {
2376 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2377 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2378 } else { // engine; cascade
2379 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2380 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2381 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2382 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2386 { ASSIGN(cps->programLogo, logoName); }
2390 UpdateLogos (int displ)
2392 if(optList[W_WHITE-1].handle == NULL) return;
2393 LoadLogo(&first, 0, 0);
2394 LoadLogo(&second, 1, appData.icsActive);
2395 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2399 void FileNamePopUpWrapper(
2410 GtkFileFilter *gtkfilter;
2411 GtkFileFilter *gtkfilter_all;
2413 char fileext[10] = "";
2414 char *result = NULL;
2416 char curDir[MSG_SIZ];
2418 StartDir(filter, NULL); // change to start directory for this file type
2420 if(def && *def && def[strlen(def)-1] == '/') {
2421 getcwd(curDir, MSG_SIZ);
2426 /* make a copy of the filter string, so that strtok can work with it*/
2427 cp = strdup(filter);
2429 /* add filters for file extensions */
2430 gtkfilter = gtk_file_filter_new();
2431 gtkfilter_all = gtk_file_filter_new();
2433 /* one filter to show everything */
2434 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2435 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2437 /* add filter if present */
2438 result = strtok(cp, space);
2439 while( result != NULL ) {
2440 snprintf(fileext,10,"*%s",result);
2441 result = strtok( NULL, space );
2442 gtk_file_filter_add_pattern(gtkfilter, fileext);
2445 /* second filter to only show what's useful */
2446 gtk_file_filter_set_name (gtkfilter,filter);
2448 if (openMode[0] == 'r')
2450 dialog = gtk_file_chooser_dialog_new (label,
2452 GTK_FILE_CHOOSER_ACTION_OPEN,
2453 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2454 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2459 dialog = gtk_file_chooser_dialog_new (label,
2461 GTK_FILE_CHOOSER_ACTION_SAVE,
2462 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2463 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2465 /* add filename suggestions */
2466 if (strlen(def) > 0 )
2467 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2469 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2473 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2474 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2475 /* activate filter */
2476 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2478 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2483 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2486 f = fopen(filename, openMode);
2489 DisplayError(_("Failed to open file"), errno);
2493 /* TODO add indec */
2495 ASSIGN(*name, filename);
2496 ScheduleDelayedEvent(DelayedLoad, 50);
2498 StartDir(filter, filename);
2501 else StartDir(filter, "");
2503 gtk_widget_destroy (dialog);
2506 if(def && *def && def[strlen(def)-1] == '/') chdir(curDir);