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 if(fontIsSet[n]) // only save fonts that were not defaults
531 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
532 if(sizeDefaults[i].squareSize == initialSquareSize) { // only for standard sizes!
533 ASSIGN(fontTable[n][initialSquareSize], name);
534 fontValid[n][initialSquareSize] = True;
537 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
538 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
543 { // nothing to do, as the sounds are at all times represented by their text-string names already
547 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
548 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
549 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
553 SaveColor (FILE *f, ArgDescriptor *ad)
554 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
555 if(colorVariable[(int)(intptr_t)ad->argLoc])
556 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
560 SaveBoardSize (FILE *f, char *name, void *addr)
561 { // wrapper to shield back-end from BoardSize & sizeInfo
562 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
566 ParseCommPortSettings (char *s)
567 { // no such option in XBoard (yet)
573 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
577 gtk_widget_get_allocation(shell, &a);
578 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
582 wp->height = a.height;
583 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
584 frameX = 3; frameY = 3; // remember to decide if windows touch
588 GetPlacement (DialogClass dlg, WindowPlacement *wp)
589 { // wrapper to shield back-end from widget type
590 if(shellUp[dlg]) GetActualPlacement(shells[dlg], wp);
595 { // wrapper to shield use of window handles from back-end (make addressible by number?)
596 // In XBoard this will have to wait until awareness of window parameters is implemented
597 GetActualPlacement(shellWidget, &wpMain);
598 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
599 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
600 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
601 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
602 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
603 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
604 GetPlacement(ChatDlg, &wpConsole); if(appData.icsActive) wpConsole.visible = shellUp[ChatDlg];
608 PrintCommPortSettings (FILE *f, char *name)
609 { // This option does not exist in XBoard
613 EnsureOnScreen (int *x, int *y, int minX, int minY)
620 { // [HGM] args: allows testing if main window is realized from back-end
621 return DialogExists(BoardWindow);
625 PopUpStartupDialog ()
626 { // start menu not implemented in XBoard
630 ConvertToLine (int argc, char **argv)
632 static char line[128*1024], buf[1024];
636 for(i=1; i<argc; i++)
638 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
639 && argv[i][0] != '{' )
640 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
642 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
643 strncat(line, buf, 128*1024 - strlen(line) - 1 );
646 line[strlen(line)-1] = NULLCHAR;
650 //--------------------------------------------------------------------------------------------
655 ResizeBoardWindow (int w, int h, int inhibit)
659 // if(clockKludge) return; // ignore as long as clock does not have final height
660 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
662 gtk_widget_get_allocation(shellWidget, &a);
663 marginW = a.width - bw;
664 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
665 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
666 // w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
667 // h += marginH + a.height + 1;
668 gtk_window_resize(GTK_WINDOW(shellWidget), w, 10);
670 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board again
675 { // dummy, as the GTK code does not make colors in advance
680 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
681 { // determine what fonts to use, and create them
683 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
684 appData.clockFont = fontTable[CLOCK_FONT][squareSize], fontIsSet[CLOCK_FONT] = True;
685 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
686 appData.font = fontTable[MESSAGE_FONT][squareSize], fontIsSet[MESSAGE_FONT] = True;
687 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
688 appData.coordFont = fontTable[COORD_FONT][squareSize], fontIsSet[COORD_FONT] = True;
689 if(!fontIsSet[CONSOLE_FONT] && fontValid[CONSOLE_FONT][squareSize])
690 appData.icsFont = fontTable[CONSOLE_FONT][squareSize], fontIsSet[CONSOLE_FONT] = True;
691 if(!fontIsSet[EDITTAGS_FONT] && fontValid[EDITTAGS_FONT][squareSize])
692 appData.tagsFont = fontTable[EDITTAGS_FONT][squareSize], fontIsSet[EDITTAGS_FONT] = True;
693 if(!fontIsSet[COMMENT_FONT] && fontValid[COMMENT_FONT][squareSize])
694 appData.commentFont = fontTable[COMMENT_FONT][squareSize], fontIsSet[COMMENT_FONT] = True;
695 if(!fontIsSet[MOVEHISTORY_FONT] && fontValid[MOVEHISTORY_FONT][squareSize])
696 appData.historyFont = fontTable[MOVEHISTORY_FONT][squareSize], fontIsSet[MOVEHISTORY_FONT] = True;
697 if(!fontIsSet[GAMELIST_FONT] && fontValid[GAMELIST_FONT][squareSize])
698 appData.gameListFont = fontTable[GAMELIST_FONT][squareSize], fontIsSet[GAMELIST_FONT] = True;
700 appData.font = InsertPxlSize(appData.font, coordFontPxlSize);
701 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
702 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
703 appData.icsFont = InsertPxlSize(appData.icsFont, coordFontPxlSize);
704 appData.tagsFont = InsertPxlSize(appData.tagsFont, coordFontPxlSize);
705 appData.commentFont = InsertPxlSize(appData.commentFont, coordFontPxlSize);
706 appData.historyFont = InsertPxlSize(appData.historyFont, coordFontPxlSize);
707 appData.gameListFont = InsertPxlSize(appData.gameListFont, coordFontPxlSize);
713 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
714 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
715 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
716 appData.font = fontTable[MESSAGE_FONT][squareSize];
717 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
718 appData.coordFont = fontTable[COORD_FONT][squareSize];
721 appData.font = InsertPxlSize(appData.font, fontPxlSize);
722 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
723 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
724 fontSet = CreateFontSet(appData.font);
725 clockFontSet = CreateFontSet(appData.clockFont);
727 /* For the coordFont, use the 0th font of the fontset. */
728 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
729 XFontStruct **font_struct_list;
730 XFontSetExtents *fontSize;
731 char **font_name_list;
732 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
733 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
734 coordFontStruct = XQueryFont(xDisplay, coordFontID);
735 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
736 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
739 appData.font = FindFont(appData.font, fontPxlSize);
740 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
741 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
742 clockFontID = XLoadFont(xDisplay, appData.clockFont);
743 clockFontStruct = XQueryFont(xDisplay, clockFontID);
744 coordFontID = XLoadFont(xDisplay, appData.coordFont);
745 coordFontStruct = XQueryFont(xDisplay, coordFontID);
746 // textHeight in !NLS mode!
748 countFontID = coordFontID; // [HGM] holdings
749 countFontStruct = coordFontStruct;
751 xdb = XtDatabase(xDisplay);
753 XrmPutLineResource(&xdb, "*international: True");
754 vTo.size = sizeof(XFontSet);
755 vTo.addr = (XtPointer) &fontSet;
756 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
758 XrmPutStringResource(&xdb, "*font", appData.font);
769 case ArgInt: p = " N"; break;
770 case ArgString: p = " STR"; break;
771 case ArgBoolean: p = " TF"; break;
772 case ArgSettingsFilename:
773 case ArgBackupSettingsFile:
774 case ArgFilename: p = " FILE"; break;
775 case ArgX: p = " Nx"; break;
776 case ArgY: p = " Ny"; break;
777 case ArgAttribs: p = " TEXTCOL"; break;
778 case ArgColor: p = " COL"; break;
779 case ArgFont: p = " FONT"; break;
780 case ArgBoardSize: p = " SIZE"; break;
781 case ArgFloat: p = " FLOAT"; break;
786 case ArgCommSettings:
798 ArgDescriptor *q, *p = argDescriptors+5;
799 printf("\nXBoard accepts the following options:\n"
800 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
801 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
802 " SIZE = board-size spec(s)\n"
803 " Within parentheses are short forms, or options to set to true or false.\n"
804 " Persistent options (saved in the settings file) are marked with *)\n\n");
806 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
807 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
808 if(p->save) strcat(buf+len, "*");
809 for(q=p+1; q->argLoc == p->argLoc; q++) {
810 if(q->argName[0] == '-') continue;
811 strcat(buf+len, q == p+1 ? " (" : " ");
812 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
814 if(q != p+1) strcat(buf+len, ")");
816 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
819 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
823 SlaveResize (Option *opt)
825 static int slaveW, slaveH, w, h;
828 gtk_widget_get_allocation(shells[DummyDlg], &a);
829 w = a.width; h = a.height;
830 gtk_widget_get_allocation(opt->handle, &a);
831 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
832 slaveH = h - a.height + 13;
834 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
838 LoadIconFile (gchar *svgFilename)
842 snprintf(buf, MSG_SIZ, "%s/%s" IMG, svgDir, svgFilename);
843 return gdk_pixbuf_new_from_file(buf, NULL);
847 static char clickedFile[MSG_SIZ];
851 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
852 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
855 if(1000*now.sec + now.ms - 1000*started.sec - started.ms < 1000) { // received during first second
856 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
857 } else { // we are running something presumably useful
859 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
860 system(buf); // start new instance on this file
865 GtkosxApplication *theApp;
869 main (int argc, char **argv)
871 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
872 int boardWidth, w, h; //, boardHeight;
874 int forceMono = False;
876 srandom(time(0)); // [HGM] book: make random truly random
878 setbuf(stdout, NULL);
879 setbuf(stderr, NULL);
882 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
883 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
887 if(argc > 1 && !strcmp(argv[1], "--help" )) {
893 gtk_init (&argc, &argv);
895 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
896 char *path = gtkosx_application_get_bundle_path();
898 char *res_path = gtkosx_application_get_resource_path();
899 snprintf(localeDir, MSG_SIZ, "%s/share/locale", res_path); // redefine locale dir for OSX bundle
901 GetTimeMark(&started); // remember start time
902 theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
903 snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
904 snprintf(dataDir, MSG_SIZ, "%s/Contents/Resources/share/xboard", path);
905 snprintf(manDir, MSG_SIZ, "%s/Contents/Resources/share/man", path);
906 snprintf(svgDir, MSG_SIZ, "%s/themes/default", dataDir);
907 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
908 g_signal_connect(theApp, "NSApplicationWillTerminate", G_CALLBACK(ExitEvent), NULL);
909 // we must call application ready before we can get the signal,
910 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
911 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
912 gtkosx_application_ready(theApp);
913 if(argc == 1) { // called without args: OSX open-file signal might follow
914 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
915 usleep(10000); // wait 10 msec (and hope this is long enough).
916 while(gtk_events_pending())
917 gtk_main_iteration(); // process all events that came in upto now
918 if(clickedFile[0]) { // we were sent an open-file signal with filename!
919 fakeArgv[0] = argv[0];
920 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
926 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
927 typedef struct {char *name, *value; } Config;
928 static Config configList[] = {
929 { "Datadir", dataDir },
930 { "Mandir", manDir },
931 { "Sysconfdir", SYSCONFDIR },
936 for(i=0; configList[i].name; i++) {
937 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
938 if(argc > 2) printf("%s", configList[i].value);
939 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
944 /* set up keyboard accelerators group */
945 GtkAccelerators = gtk_accel_group_new();
947 programName = strrchr(argv[0], '/');
948 if (programName == NULL)
949 programName = argv[0];
954 // if (appData.debugMode) {
955 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
958 bindtextdomain(PACKAGE, LOCALEDIR);
959 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
963 appData.boardSize = "";
964 InitAppData(ConvertToLine(argc, argv));
966 if (p == NULL) p = "/tmp";
967 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
968 gameCopyFilename = (char*) malloc(i);
969 gamePasteFilename = (char*) malloc(i);
970 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
971 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
973 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
974 static char buf[MSG_SIZ];
975 snprintf(buf, MSG_SIZ, appData.sysOpen, dataDir);
976 ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
977 EscapeExpand(buf, appData.firstInitString);
978 appData.firstInitString = strdup(buf);
979 EscapeExpand(buf, appData.secondInitString);
980 appData.secondInitString = strdup(buf);
981 EscapeExpand(buf, appData.firstComputerString);
982 appData.firstComputerString = strdup(buf);
983 EscapeExpand(buf, appData.secondComputerString);
984 appData.secondComputerString = strdup(buf);
987 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
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;
1157 formWidget = XtParent(boardWidget);
1158 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1159 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1160 XtGetValues(optList[W_WHITE].handle, args, 2);
1161 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1162 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1163 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1164 XtGetValues(optList[W_PAUSE].handle, args, 2);
1168 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1169 // not need to go into InitDrawingSizes().
1173 // add accelerators to main shell
1174 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1177 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1179 WhiteIcon = LoadIconFile("icon_white");
1180 BlackIcon = LoadIconFile("icon_black");
1181 SetClockIcon(0); // sets white icon
1185 * Create a cursor for the board widget.
1188 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1189 XChangeWindowAttributes(xDisplay, xBoardWindow,
1190 CWCursor, &window_attributes);
1194 * Inhibit shell resizing.
1197 shellArgs[0].value = (XtArgVal) &w;
1198 shellArgs[1].value = (XtArgVal) &h;
1199 XtGetValues(shellWidget, shellArgs, 2);
1200 shellArgs[4].value = shellArgs[2].value = w;
1201 shellArgs[5].value = shellArgs[3].value = h;
1202 // XtSetValues(shellWidget, &shellArgs[2], 4);
1205 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1206 // It wil only become known asynchronously, when we first write a string into it.
1207 // This will then change the clock widget height, which triggers resizing the top-level window
1208 // and a configure event. Only then can we know the total height of the top-level window,
1209 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1210 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1213 gtk_widget_get_allocation(shells[BoardWindow], &a);
1214 w = a.width; h = a.height;
1215 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1216 clockKludge = hc = a.height;
1217 gtk_widget_get_allocation(boardWidget, &a);
1218 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1219 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1225 if(appData.logoSize)
1226 { // locate and read user logo
1227 char buf[MSG_SIZ], name[MSG_SIZ];
1228 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1229 if(!FindLogo(name, ".logo", buf))
1230 FindLogo(appData.logoDir, name + 6, buf);
1231 ASSIGN(userLogo, buf);
1234 if (appData.animate || appData.animateDragging)
1237 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1238 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1240 /* [AS] Restore layout */
1241 if( wpMoveHistory.visible ) {
1245 if( wpEvalGraph.visible )
1250 if( wpEngineOutput.visible ) {
1251 EngineOutputPopUp();
1254 if( wpConsole.visible && appData.icsActive ) {
1259 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1264 if (errorExitStatus == -1) {
1265 if (appData.icsActive) {
1266 /* We now wait until we see "login:" from the ICS before
1267 sending the logon script (problems with timestamp otherwise) */
1268 /*ICSInitScript();*/
1269 if (appData.icsInputBox) ICSInputBoxPopUp();
1273 signal(SIGWINCH, TermSizeSigHandler);
1275 signal(SIGINT, IntSigHandler);
1276 signal(SIGTERM, IntSigHandler);
1277 if (*appData.cmailGameName != NULLCHAR) {
1278 signal(SIGUSR1, CmailSigHandler);
1283 // XtSetKeyboardFocus(shellWidget, formWidget);
1285 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1288 /* check for GTK events and process them */
1291 gtk_main_iteration();
1294 if (appData.debugMode) fclose(debugFP); // [DM] debug
1301 while(gtk_events_pending()) gtk_main_iteration();
1305 TermSizeSigHandler (int sig)
1311 IntSigHandler (int sig)
1317 CmailSigHandler (int sig)
1322 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1324 /* Activate call-back function CmailSigHandlerCallBack() */
1325 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1327 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1331 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1334 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1336 /**** end signal code ****/
1339 #define Abs(n) ((n)<0 ? -(n) : (n))
1342 InsertPxlSize (char *pattern, int targetPxlSize)
1345 snprintf(buf, MSG_SIZ, pattern, targetPxlSize); // pattern is something like "Sans Bold %d"
1352 InsertPxlSize (char *pattern, int targetPxlSize)
1354 char *base_fnt_lst, strInt[12], *p, *q;
1355 int alternatives, i, len, strIntLen;
1358 * Replace the "*" (if present) in the pixel-size slot of each
1359 * alternative with the targetPxlSize.
1363 while ((p = strchr(p, ',')) != NULL) {
1367 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1368 strIntLen = strlen(strInt);
1369 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1373 while (alternatives--) {
1374 char *comma = strchr(p, ',');
1375 for (i=0; i<14; i++) {
1376 char *hyphen = strchr(p, '-');
1378 if (comma && hyphen > comma) break;
1379 len = hyphen + 1 - p;
1380 if (i == 7 && *p == '*' && len == 2) {
1382 memcpy(q, strInt, strIntLen);
1392 len = comma + 1 - p;
1399 return base_fnt_lst;
1405 CreateFontSet (char *base_fnt_lst)
1408 char **missing_list;
1412 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1413 &missing_list, &missing_count, &def_string);
1414 if (appData.debugMode) {
1416 XFontStruct **font_struct_list;
1417 char **font_name_list;
1418 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1420 fprintf(debugFP, " got list %s, locale %s\n",
1421 XBaseFontNameListOfFontSet(fntSet),
1422 XLocaleOfFontSet(fntSet));
1423 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1424 for (i = 0; i < count; i++) {
1425 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1428 for (i = 0; i < missing_count; i++) {
1429 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1432 if (fntSet == NULL) {
1433 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1439 #else // not ENABLE_NLS
1441 * Find a font that matches "pattern" that is as close as
1442 * possible to the targetPxlSize. Prefer fonts that are k
1443 * pixels smaller to fonts that are k pixels larger. The
1444 * pattern must be in the X Consortium standard format,
1445 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1446 * The return value should be freed with XtFree when no
1451 FindFont (char *pattern, int targetPxlSize)
1453 char **fonts, *p, *best, *scalable, *scalableTail;
1454 int i, j, nfonts, minerr, err, pxlSize;
1456 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1458 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1459 programName, pattern);
1466 for (i=0; i<nfonts; i++) {
1469 if (*p != '-') continue;
1471 if (*p == NULLCHAR) break;
1472 if (*p++ == '-') j++;
1474 if (j < 7) continue;
1477 scalable = fonts[i];
1480 err = pxlSize - targetPxlSize;
1481 if (Abs(err) < Abs(minerr) ||
1482 (minerr > 0 && err < 0 && -err == minerr)) {
1488 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1489 /* If the error is too big and there is a scalable font,
1490 use the scalable font. */
1491 int headlen = scalableTail - scalable;
1492 p = (char *) XtMalloc(strlen(scalable) + 10);
1493 while (isdigit(*scalableTail)) scalableTail++;
1494 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1496 p = (char *) XtMalloc(strlen(best) + 2);
1497 safeStrCpy(p, best, strlen(best)+1 );
1499 if (appData.debugMode) {
1500 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1501 pattern, targetPxlSize, p);
1503 XFreeFontNames(fonts);
1510 MarkMenuItem (char *menuRef, int state)
1512 MenuItem *item = MenuNameToItem(menuRef);
1514 if(item && item->handle) {
1515 ((GtkCheckMenuItem *) (item->handle))->active = state;
1521 EnableNamedMenuItem (char *menuRef, int state)
1523 MenuItem *item = MenuNameToItem(menuRef);
1525 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1530 EnableButtonBar (int state)
1533 XtSetSensitive(optList[W_BUTTON].handle, state);
1539 SetMenuEnables (Enables *enab)
1541 while (enab->name != NULL) {
1542 EnableNamedMenuItem(enab->name, enab->value);
1547 gboolean KeyPressProc(window, eventkey, data)
1549 GdkEventKey *eventkey;
1553 MoveTypeInProc(eventkey); // pop up for typed in moves
1556 /* check for other key values */
1557 switch(eventkey->keyval) {
1569 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1570 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1572 if(*nprms == 0) return;
1573 item = MenuNameToItem(prms[0]);
1574 if(item) ((MenuProc *) item->proc) ();
1588 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1589 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1590 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1591 dmEnables[i].piece);
1592 XtSetSensitive(entry, p != NULL || !appData.testLegality
1593 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1594 && !appData.icsActive));
1596 while (p && *p++ == dmEnables[i].piece) count++;
1597 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1599 XtSetArg(args[j], XtNlabel, label); j++;
1600 XtSetValues(entry, args, j);
1606 do_flash_delay (unsigned long msec)
1612 FlashDelay (int flash_delay)
1614 if(flash_delay) do_flash_delay(flash_delay);
1618 Fraction (int x, int start, int stop)
1620 double f = ((double) x - start)/(stop - start);
1621 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1625 static WindowPlacement wpNew;
1628 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1630 int touch=0, fudge = 4, f = 3;
1631 GetActualPlacement(sh, wp);
1632 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1633 if(abs(wp->x + wp->width + 2*frameX - f - wpMain.x) < fudge) touch = 2; else // left touch
1634 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1635 if(abs(wp->y + wp->height + frameX + frameY - f - wpMain.y) < fudge) touch = 4; // top touch
1636 //printf("CoDrag: touch = %d x=%d w=%d x2=%d w2=%d fx=%d\n", touch, wpMain.x, wpMain.width, wp->x, wp->width, frameX);
1637 if(!touch ) return; // only windows that touch co-move
1638 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1639 int heightInc = wpNew.height - wpMain.height;
1640 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1641 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1642 wp->y += fracTop * heightInc;
1643 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1645 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1647 wp->height += heightInc;
1648 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1649 int widthInc = wpNew.width - wpMain.width;
1650 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1651 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1652 wp->y += fracLeft * widthInc;
1653 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1655 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1657 wp->width += widthInc;
1659 wp->x += wpNew.x - wpMain.x;
1660 wp->y += wpNew.y - wpMain.y;
1661 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1662 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1664 XtSetArg(args[j], XtNx, wp->x); j++;
1665 XtSetArg(args[j], XtNy, wp->y); j++;
1666 XtSetValues(sh, args, j);
1668 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1669 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1670 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1674 ReSize (WindowPlacement *wp)
1677 int sqx, sqy, i, w, h, lg = lineGap;
1678 static int first = 1;
1679 // DisplayBothClocks();
1680 if(wp->width == wpMain.width && wp->height == wpMain.height && !first) return; // not sized
1681 gtk_widget_get_allocation(optList[W_DROP+1].handle, &a); // table that should contain everything
1682 w = a.width; h = a.height;
1683 gtk_widget_get_allocation(shellWidget, &a);
1684 if(a.width < w || a.height < h) { // outer window smaller than dialog content?
1685 w = a.width - w; h = a.height - h; // subtract matrgins, measured as table minus board dimensions
1686 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1687 w += a.width; h += a.height;
1689 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1690 w = a.width; h = a.height;
1692 sqx = (w - lg) / BOARD_WIDTH - lg;
1693 sqy = (h - lg) / BOARD_HEIGHT - lg;
1694 if(sqy < sqx) sqx = sqy;
1695 if(sqx < 20) return;
1696 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1698 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1699 sqx = (w - lg) / BOARD_WIDTH - lg;
1700 sqy = (h - lg) / BOARD_HEIGHT - lg;
1701 if(sqy < sqx) sqx = sqy;
1702 lg = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1703 if(sqx == oldSqx + 1 && lg == lineGap + 1) sqx = oldSqx, squareSize = 0; // prevent oscillations, force resize by kludge
1705 for(h=0; sizeDefaults[h].name && sizeDefaults[h].squareSize*8 > sqx*BOARD_WIDTH; h++) {}
1706 if(initialSquareSize != sizeDefaults[h].squareSize) { // boardSize changed
1707 initialSquareSize = sizeDefaults[h].squareSize; // used for saving font
1708 ChangeFont(1, &appData.clockFont, CLOCK_FONT, initialSquareSize, CLOCK_FONT_NAME, 2*(sizeDefaults[h].clockFontPxlSize+1)/3);
1709 ChangeFont(1, &appData.font, MESSAGE_FONT, initialSquareSize, DEFAULT_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1710 ChangeFont(0, &appData.icsFont, CONSOLE_FONT, initialSquareSize, CONSOLE_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1711 ChangeFont(0, &appData.tagsFont, EDITTAGS_FONT, initialSquareSize, TAGS_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1712 ChangeFont(0, &appData.commentFont, COMMENT_FONT, initialSquareSize, COMMENT_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1713 ChangeFont(0, &appData.gameListFont, GAMELIST_FONT, initialSquareSize, GAMELIST_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1714 ChangeFont(0, &appData.coordFont, MOVEHISTORY_FONT, initialSquareSize, HISTORY_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1715 DisplayBothClocks();
1716 ApplyFont(&mainOptions[W_MESSG], NULL);
1717 for(i=1; i<6; i++) ApplyFont(&mainOptions[W_BUTTON+i], NULL);
1719 if(!strchr(appData.boardSize, ',')) {
1720 ASSIGN(appData.boardSize, sizeDefaults[h].name);
1723 if(sizeDefaults[h].tinyLayout != tinyLayout) { // alter clipping of menu names to conform to board width
1724 int clip = (tinyLayout = sizeDefaults[h].tinyLayout) + 1;
1726 for(h=1; mainOptions[h].type == DropDown; h++) {
1727 strncpy(text, _(mainOptions[h].name), MSG_SIZ);
1728 if(clip != 1) text[clip + (text[clip-1] == '_')] = NULLCHAR;
1729 gtk_menu_item_set_label((GtkMenuItem *) mainOptions[h].handle, text);
1733 if(sqx != squareSize && !first) {
1734 squareSize = sqx; // adopt new square size
1735 CreatePNGPieces(); // make newly scaled pieces
1736 InitDrawingSizes(0, 0); // creates grid etc.
1737 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1738 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1739 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1740 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1741 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1742 first = appData.fixedSize;
1743 if(twoBoards && shellUp[DummyDlg]) {
1744 SlavePopUp(); dualOptions[3].max = 0; DoEvents(); // calls SlaveResize, kludge to force assigning new canvas
1745 partnerUp = !partnerUp; flipView = !flipView;
1746 DrawPosition(True, NULL);
1747 partnerUp = !partnerUp; flipView = !flipView;
1751 static guint delayedDragTag = 0;
1757 if(busy) { // prevent recursive calling, but postpone interrupting call rather than lose it
1758 if(!delayedDragTag) delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1762 GetActualPlacement(shellWidget, &wpNew);
1763 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1764 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1765 busy = 0; return; // false alarm
1768 if(appData.useStickyWindows) {
1769 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1770 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1771 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1772 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1773 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1776 DrawPosition(True, NULL);
1777 if(delayedDragTag) g_source_remove(delayedDragTag);
1778 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1785 //printf("old timr = %d\n", delayedDragTag);
1786 if(delayedDragTag) g_source_remove(delayedDragTag);
1787 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1788 //printf("new timr = %d\n", delayedDragTag);
1792 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1794 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1796 wpNew.x = event->configure.x;
1797 wpNew.y = event->configure.y;
1798 wpNew.width = event->configure.width;
1799 wpNew.height = event->configure.height;
1800 // SetWidgetLabel(&mainOptions[W_WHITE], ""); SetWidgetLabel(&mainOptions[W_BLACK], "");
1801 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1807 /* Disable all user input other than deleting the window */
1808 static int frozen = 0;
1814 /* Grab by a widget that doesn't accept input */
1815 gtk_grab_add(optList[W_MESSG].handle);
1819 /* Undo a FreezeUI */
1823 if (!frozen) return;
1824 gtk_grab_remove(optList[W_MESSG].handle);
1831 static int oldPausing = FALSE;
1832 static GameMode oldMode = (GameMode) -1;
1834 if (!boardWidget) return;
1836 if (pausing != oldPausing) {
1837 oldPausing = pausing;
1838 MarkMenuItem("Mode.Pause", pausing);
1840 if (appData.showButtonBar) {
1841 /* Always toggle, don't set. Previous code messes up when
1842 invoked while the button is pressed, as releasing it
1843 toggles the state again. */
1845 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1846 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1850 wname = ModeToWidgetName(oldMode);
1851 if (wname != NULL) {
1852 MarkMenuItem(wname, False);
1854 wname = ModeToWidgetName(gameMode);
1855 if (wname != NULL) {
1856 MarkMenuItem(wname, True);
1858 if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
1859 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1862 /* Maybe all the enables should be handled here, not just this one */
1863 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1865 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1870 * Button/menu procedures
1873 void CopyFileToClipboard(gchar *filename)
1875 gchar *selection_tmp;
1879 FILE* f = fopen(filename, "r");
1882 if (f == NULL) return;
1886 selection_tmp = g_try_malloc(len + 1);
1887 if (selection_tmp == NULL) {
1888 printf("Malloc failed in CopyFileToClipboard\n");
1891 count = fread(selection_tmp, 1, len, f);
1894 g_free(selection_tmp);
1897 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1899 // copy selection_tmp to clipboard
1900 GdkDisplay *gdisp = gdk_display_get_default();
1902 g_free(selection_tmp);
1905 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1906 gtk_clipboard_set_text(cb, selection_tmp, -1);
1907 g_free(selection_tmp);
1911 CopySomething (char *src)
1913 GdkDisplay *gdisp = gdk_display_get_default();
1915 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1916 if (gdisp == NULL) return;
1917 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1918 gtk_clipboard_set_text(cb, src, -1);
1922 PastePositionProc ()
1924 GdkDisplay *gdisp = gdk_display_get_default();
1928 if (gdisp == NULL) return;
1929 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1930 fenstr = gtk_clipboard_wait_for_text(cb);
1931 if (fenstr==NULL) return; // nothing had been selected to copy
1932 EditPositionPasteFEN(fenstr);
1941 guint len=0; int flip = appData.flipView;
1944 // get game from clipboard
1945 GdkDisplay *gdisp = gdk_display_get_default();
1946 if (gdisp == NULL) return;
1947 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1948 text = gtk_clipboard_wait_for_text(cb);
1949 if (text == NULL) return; // nothing to paste
1952 // write to temp file
1953 if (text == NULL || len == 0) {
1954 return; //nothing to paste
1956 f = fopen(gamePasteFilename, "w");
1958 DisplayError(_("Can't open temp file"), errno);
1961 fwrite(text, 1, len, f);
1965 if(!appData.autoFlipView) appData.flipView = flipView;
1966 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1967 appData.flipView = flip;
1974 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1980 void MoveTypeInProc(eventkey)
1981 GdkEventKey *eventkey;
1985 // ingnore if ctrl, alt, or meta is pressed
1986 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1990 buf[0]=eventkey->keyval;
1992 if (eventkey->keyval > 32 && eventkey->keyval < 256 || *buf == 27)
1993 ConsoleAutoPopUp (buf);
1998 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2000 if (!TempBackwardActive) {
2001 TempBackwardActive = True;
2007 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2009 /* Check to see if triggered by a key release event for a repeating key.
2010 * If so the next queued event will be a key press of the same key at the same time */
2011 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2013 XPeekEvent(xDisplay, &next);
2014 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2015 next.xkey.keycode == event->xkey.keycode)
2019 TempBackwardActive = False;
2025 { // called from menu
2028 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"man %s/../man/man6/xboard.6\"' -e 'end tell'", dataDir);
2031 system("xterm -e man xboard &");
2040 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);
2042 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
2050 SetWindowTitle (char *text, char *title, char *icon)
2055 if (appData.titleInWindow) {
2057 XtSetArg(args[i], XtNlabel, text); i++;
2058 XtSetValues(titleWidget, args, i);
2061 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2062 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2063 XtSetValues(shellWidget, args, i);
2064 XSync(xDisplay, False);
2066 if (appData.titleInWindow) {
2067 SetWidgetLabel(titleWidget, text);
2069 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2074 DisplayIcsInteractionTitle (String message)
2077 if (oldICSInteractionTitle == NULL) {
2078 /* Magic to find the old window title, adapted from vim */
2079 char *wina = getenv("WINDOWID");
2081 Window win = (Window) atoi(wina);
2082 Window root, parent, *children;
2083 unsigned int nchildren;
2084 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2086 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2087 if (!XQueryTree(xDisplay, win, &root, &parent,
2088 &children, &nchildren)) break;
2089 if (children) XFree((void *)children);
2090 if (parent == root || parent == 0) break;
2093 XSetErrorHandler(oldHandler);
2095 if (oldICSInteractionTitle == NULL) {
2096 oldICSInteractionTitle = "xterm";
2099 printf("\033]0;%s\007", message);
2105 LockBoardSize (int after)
2107 static char *oldClockFont, *oldMessgFont;
2109 if(oldMessgFont && !strcmp(oldMessgFont, appData.font) &&
2110 oldClockFont && !strcmp(oldClockFont, appData.clockFont) && after < 2) return; // only do something when font changed
2111 w = BOARD_WIDTH*(squareSize + lineGap) + lineGap;
2112 h = BOARD_HEIGHT*(squareSize + lineGap) + lineGap;
2114 ASSIGN(oldClockFont, appData.clockFont);
2115 ASSIGN(oldMessgFont, appData.font);
2116 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
2118 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board
2120 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
2125 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2127 static int twoLines = -1;
2128 GtkWidget *w = (GtkWidget *) opt->handle;
2130 char *markup, two = (appData.logoSize != 0);
2135 strcpy(bgcolor, "black");
2136 strcpy(fgcolor, "white");
2138 strcpy(bgcolor, "white");
2139 strcpy(fgcolor, "black");
2142 appData.lowTimeWarning &&
2143 (timer / 1000) < appData.icsAlarmTime) {
2144 strcpy(fgcolor, appData.lowTimeWarningColor);
2147 if(! partnerUp && two != twoLines) LockBoardSize(2); // lock board size if clock height changes
2149 gdk_color_parse( bgcolor, &col );
2150 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2152 if (appData.clockMode) {
2153 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2154 bgcolor, fgcolor, color, two ? "\n" : " ", TimeString(timer));
2155 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2156 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2158 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2159 bgcolor, fgcolor, color);
2160 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2161 // bgcolor, fgcolor, color);
2163 gtk_label_set_markup(GTK_LABEL(w), markup);
2166 if(!partnerUp && two != twoLines) LockBoardSize(3), twoLines = two;
2169 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2172 SetClockIcon (int color)
2174 GdkPixbuf *pm = *clockIcons[color];
2175 if (mainwindowIcon != pm) {
2176 mainwindowIcon = pm;
2178 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2180 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2185 #define INPUT_SOURCE_BUF_SIZE 8192
2194 char buf[INPUT_SOURCE_BUF_SIZE];
2199 DoInputCallback(io, cond, data)
2204 /* read input from one of the input source (for example a chess program, ICS, etc).
2205 * and call a function that will handle the input
2212 /* All information (callback function, file descriptor, etc) is
2213 * saved in an InputSource structure
2215 InputSource *is = (InputSource *) data;
2217 if (is->lineByLine) {
2218 count = read(is->fd, is->unused,
2219 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2221 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2222 RemoveInputSource(is); // cease reading stdin
2223 stdoutClosed = TRUE; // suppress future output
2226 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2229 is->unused += count;
2231 /* break input into lines and call the callback function on each
2234 while (p < is->unused) {
2235 q = memchr(p, '\n', is->unused - p);
2236 if (q == NULL) break;
2238 (is->func)(is, is->closure, p, q - p, 0);
2241 /* remember not yet used part of the buffer */
2243 while (p < is->unused) {
2248 /* read maximum length of input buffer and send the whole buffer
2249 * to the callback function
2251 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2256 (is->func)(is, is->closure, is->buf, count, error);
2258 return True; // Must return true or the watch will be removed
2261 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2268 GIOChannel *channel;
2269 ChildProc *cp = (ChildProc *) pr;
2271 is = (InputSource *) calloc(1, sizeof(InputSource));
2272 is->lineByLine = lineByLine;
2276 is->fd = fileno(stdin);
2278 is->kind = cp->kind;
2279 is->fd = cp->fdFrom;
2282 is->unused = is->buf;
2286 /* GTK-TODO: will this work on windows?*/
2288 channel = g_io_channel_unix_new(is->fd);
2289 g_io_channel_set_close_on_unref (channel, TRUE);
2290 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2292 is->closure = closure;
2293 return (InputSourceRef) is;
2298 RemoveInputSource(isr)
2301 InputSource *is = (InputSource *) isr;
2303 if (is->sid == 0) return;
2304 g_source_remove(is->sid);
2311 static Boolean frameWaiting;
2314 FrameAlarm (int sig)
2316 frameWaiting = False;
2317 /* In case System-V style signals. Needed?? */
2318 signal(SIGALRM, FrameAlarm);
2322 FrameDelay (int time)
2324 struct itimerval delay;
2327 frameWaiting = True;
2328 signal(SIGALRM, FrameAlarm);
2329 delay.it_interval.tv_sec =
2330 delay.it_value.tv_sec = time / 1000;
2331 delay.it_interval.tv_usec =
2332 delay.it_value.tv_usec = (time % 1000) * 1000;
2333 setitimer(ITIMER_REAL, &delay, NULL);
2334 while (frameWaiting) pause();
2335 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2336 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2337 setitimer(ITIMER_REAL, &delay, NULL);
2344 FrameDelay (int time)
2347 XSync(xDisplay, False);
2349 // gtk_main_iteration_do(False);
2352 usleep(time * 1000);
2358 FindLogo (char *place, char *name, char *buf)
2359 { // check if file exists in given place
2361 if(!place) return 0;
2362 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2363 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2371 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2373 char buf[MSG_SIZ], *logoName = buf;
2374 if(appData.logo[n][0]) {
2375 logoName = appData.logo[n];
2376 } else if(appData.autoLogo) {
2377 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2378 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2379 } else { // engine; cascade
2380 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2381 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2382 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2383 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2387 { ASSIGN(cps->programLogo, logoName); }
2391 UpdateLogos (int displ)
2393 if(optList[W_WHITE-1].handle == NULL) return;
2394 LoadLogo(&first, 0, 0);
2395 LoadLogo(&second, 1, appData.icsActive);
2396 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2400 void FileNamePopUpWrapper(
2411 GtkFileFilter *gtkfilter;
2412 GtkFileFilter *gtkfilter_all;
2414 char fileext[10] = "";
2415 char *result = NULL;
2417 char curDir[MSG_SIZ];
2419 StartDir(filter, NULL); // change to start directory for this file type
2421 if(def && *def && def[strlen(def)-1] == '/') {
2422 getcwd(curDir, MSG_SIZ);
2427 /* make a copy of the filter string, so that strtok can work with it*/
2428 cp = strdup(filter);
2430 /* add filters for file extensions */
2431 gtkfilter = gtk_file_filter_new();
2432 gtkfilter_all = gtk_file_filter_new();
2434 /* one filter to show everything */
2435 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2436 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2438 /* add filter if present */
2439 result = strtok(cp, space);
2440 while( result != NULL ) {
2441 snprintf(fileext,10,"*%s",result);
2442 result = strtok( NULL, space );
2443 gtk_file_filter_add_pattern(gtkfilter, fileext);
2446 /* second filter to only show what's useful */
2447 gtk_file_filter_set_name (gtkfilter,filter);
2449 if (openMode[0] == 'r')
2451 dialog = gtk_file_chooser_dialog_new (label,
2453 GTK_FILE_CHOOSER_ACTION_OPEN,
2454 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2455 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2460 dialog = gtk_file_chooser_dialog_new (label,
2462 GTK_FILE_CHOOSER_ACTION_SAVE,
2463 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2464 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2466 /* add filename suggestions */
2467 if (strlen(def) > 0 )
2468 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2470 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2474 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2475 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2476 /* activate filter */
2477 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2479 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2484 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2487 f = fopen(filename, openMode);
2490 DisplayError(_("Failed to open file"), errno);
2494 /* TODO add indec */
2496 ASSIGN(*name, filename);
2497 ScheduleDelayedEvent(DelayedLoad, 50);
2499 StartDir(filter, filename);
2502 else StartDir(filter, "");
2504 gtk_widget_destroy (dialog);
2507 if(def && *def && def[strlen(def)-1] == '/') chdir(curDir);