2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
9 * Software Foundation, Inc.
11 * The following terms apply to Digital Equipment Corporation's copyright
13 * ------------------------------------------------------------------------
16 * Permission to use, copy, modify, and distribute this software and its
17 * documentation for any purpose and without fee is hereby granted,
18 * provided that the above copyright notice appear in all copies and that
19 * both that copyright notice and this permission notice appear in
20 * supporting documentation, and that the name of Digital not be
21 * used in advertising or publicity pertaining to distribution of the
22 * software without specific, written prior permission.
24 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
26 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
27 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
28 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
29 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
31 * ------------------------------------------------------------------------
33 * The following terms apply to the enhanced version of XBoard
34 * distributed by the Free Software Foundation:
35 * ------------------------------------------------------------------------
37 * GNU XBoard is free software: you can redistribute it and/or modify
38 * it under the terms of the GNU General Public License as published by
39 * the Free Software Foundation, either version 3 of the License, or (at
40 * your option) any later version.
42 * GNU XBoard is distributed in the hope that it will be useful, but
43 * WITHOUT ANY WARRANTY; without even the implied warranty of
44 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
45 * General Public License for more details.
47 * You should have received a copy of the GNU General Public License
48 * along with this program. If not, see http://www.gnu.org/licenses/. *
50 *------------------------------------------------------------------------
51 ** See the file ChangeLog for a revision history. */
61 #include <sys/types.h>
65 #include <cairo/cairo.h>
69 # if HAVE_SYS_SOCKET_H
70 # include <sys/socket.h>
71 # include <netinet/in.h>
73 # else /* not HAVE_SYS_SOCKET_H */
74 # if HAVE_LAN_SOCKET_H
75 # include <lan/socket.h>
77 # include <lan/netdb.h>
78 # else /* not HAVE_LAN_SOCKET_H */
79 # define OMIT_SOCKETS 1
80 # endif /* not HAVE_LAN_SOCKET_H */
81 # endif /* not HAVE_SYS_SOCKET_H */
82 #endif /* !OMIT_SOCKETS */
87 #else /* not STDC_HEADERS */
88 extern char *getenv();
91 # else /* not HAVE_STRING_H */
93 # endif /* not HAVE_STRING_H */
94 #endif /* not STDC_HEADERS */
97 # include <sys/fcntl.h>
98 #else /* not HAVE_SYS_FCNTL_H */
101 # endif /* HAVE_FCNTL_H */
102 #endif /* not HAVE_SYS_FCNTL_H */
104 #if HAVE_SYS_SYSTEMINFO_H
105 # include <sys/systeminfo.h>
106 #endif /* HAVE_SYS_SYSTEMINFO_H */
108 #if TIME_WITH_SYS_TIME
109 # include <sys/time.h>
113 # include <sys/time.h>
124 # include <sys/wait.h>
129 # define NAMLEN(dirent) strlen((dirent)->d_name)
130 # define HAVE_DIR_STRUCT
132 # define dirent direct
133 # define NAMLEN(dirent) (dirent)->d_namlen
135 # include <sys/ndir.h>
136 # define HAVE_DIR_STRUCT
139 # include <sys/dir.h>
140 # define HAVE_DIR_STRUCT
144 # define HAVE_DIR_STRUCT
152 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
155 #include "frontend.h"
157 #include "backendz.h"
165 #include "engineoutput.h"
171 # include <gtkmacintegration/gtkosxapplication.h>
172 // prevent pathname of positional file argument provided by OS X being be mistaken for option name
173 // (price is that we won't recognize Windows option format anymore).
176 // redefine some defaults
179 # undef SETTINGS_FILE
180 # define ICS_LOGON "Library/Preferences/XboardICS.conf"
181 # define LOCALEDIR localeDir
182 # define SETTINGS_FILE masterSettings
183 # define SYNC_MENUBAR gtkosx_application_sync_menubar(theApp)
184 char localeDir[MSG_SIZ];
185 char masterSettings[MSG_SIZ];
189 # define SYNC_MENUBAR
196 #define usleep(t) _sleep2(((t)+500)/1000)
200 # define _(s) gettext (s)
201 # define N_(s) gettext_noop (s)
207 int main P((int argc, char **argv));
208 RETSIGTYPE CmailSigHandler P((int sig));
209 RETSIGTYPE IntSigHandler P((int sig));
210 RETSIGTYPE TermSizeSigHandler P((int sig));
211 char *InsertPxlSize P((char *pattern, int targetPxlSize));
214 XFontSet CreateFontSet P((char *base_fnt_lst));
216 char *FindFont P((char *pattern, int targetPxlSize));
219 void DelayedDrag P((void));
220 void ICSInputBoxPopUp P((void));
221 void MoveTypeInProc P((GdkEventKey *eventkey));
222 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
223 Boolean TempBackwardActive = False;
224 void DisplayMove P((int moveNumber));
225 void update_ics_width P(());
226 int CopyMemoProc P(());
227 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
228 static int FindLogo P((char *place, char *name, char *buf));
232 XFontSet fontSet, clockFontSet;
235 XFontStruct *clockFontStruct;
237 Font coordFontID, countFontID;
238 XFontStruct *coordFontStruct, *countFontStruct;
240 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
241 GtkWidget *mainwindow;
243 Option *optList; // contains all widgets of main window
246 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
249 static GdkPixbuf *mainwindowIcon=NULL;
250 static GdkPixbuf *WhiteIcon=NULL;
251 static GdkPixbuf *BlackIcon=NULL;
253 /* key board accelerators */
254 GtkAccelGroup *GtkAccelerators;
256 typedef unsigned int BoardSize;
258 Boolean chessProgram;
259 int initialSquareSize;
261 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
262 int smallLayout = 0, tinyLayout = 0,
263 marginW, marginH, // [HGM] for run-time resizing
264 fromX = -1, fromY = -1, toX, toY, commentUp = False,
265 errorExitStatus = -1, defaultLineGap;
267 Dimension textHeight;
269 char *chessDir, *programName, *programVersion;
270 Boolean alwaysOnTop = False;
271 char *icsTextMenuString;
273 char *firstChessProgramNames;
274 char *secondChessProgramNames;
276 WindowPlacement wpMain;
277 WindowPlacement wpConsole;
278 WindowPlacement wpComment;
279 WindowPlacement wpMoveHistory;
280 WindowPlacement wpEvalGraph;
281 WindowPlacement wpEngineOutput;
282 WindowPlacement wpGameList;
283 WindowPlacement wpTags;
284 WindowPlacement wpDualBoard;
286 /* This magic number is the number of intermediate frames used
287 in each half of the animation. For short moves it's reduced
288 by 1. The total number of frames will be factor * 2 + 1. */
291 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
298 DropMenuEnables dmEnables[] = {
307 XtResource clientResources[] = {
308 { "flashCount", "flashCount", XtRInt, sizeof(int),
309 XtOffset(AppDataPtr, flashCount), XtRImmediate,
310 (XtPointer) FLASH_COUNT },
314 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
315 char globalTranslations[] =
316 ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
317 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
318 :<KeyDown>Return: TempBackwardProc() \n \
319 :<KeyUp>Return: TempForwardProc() \n";
321 char ICSInputTranslations[] =
322 "<Key>Up: UpKeyProc() \n "
323 "<Key>Down: DownKeyProc() \n "
324 "<Key>Return: EnterKeyProc() \n";
326 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
327 // as the widget is destroyed before the up-click can call extend-end
328 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
331 String xboardResources[] = {
332 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
340 gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
343 //---------------------------------------------------------------------------------------------------------
344 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
347 #define CW_USEDEFAULT (1<<31)
348 #define ICS_TEXT_MENU_SIZE 90
349 #define DEBUG_FILE "xboard.debug"
350 #define SetCurrentDirectory chdir
351 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
355 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
358 // front-end part of option handling
360 // [HGM] This platform-dependent table provides the location for storing the color info
361 extern char *crWhite, * crBlack;
365 &appData.whitePieceColor,
366 &appData.blackPieceColor,
367 &appData.lightSquareColor,
368 &appData.darkSquareColor,
369 &appData.highlightSquareColor,
370 &appData.premoveHighlightColor,
371 &appData.lowTimeWarningColor,
382 // [HGM] font: keep a font for each square size, even non-stndard ones
384 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
385 char *fontTable[NUM_FONTS][MAX_SIZE];
388 ParseFont (char *name, int number)
389 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
391 if(sscanf(name, "size%d:", &size)) {
392 // [HGM] font: font is meant for specific boardSize (likely from settings file);
393 // defer processing it until we know if it matches our board size
394 if(!strstr(name, "-*-") && // ignore X-fonts
395 size >= 0 && size<MAX_SIZE) { // for now, fixed limit
396 fontTable[number][size] = strdup(strchr(name, ':')+1);
397 fontValid[number][size] = True;
402 case 0: // CLOCK_FONT
403 appData.clockFont = strdup(name);
405 case 1: // MESSAGE_FONT
406 appData.font = strdup(name);
408 case 2: // COORD_FONT
409 appData.coordFont = strdup(name);
412 appData.icsFont = strdup(name);
415 appData.tagsFont = strdup(name);
418 appData.commentFont = strdup(name);
420 case MOVEHISTORY_FONT:
421 appData.historyFont = strdup(name);
424 appData.gameListFont = strdup(name);
429 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
434 { // only 2 fonts currently
435 appData.clockFont = strdup(CLOCK_FONT_NAME);
436 appData.coordFont = strdup(COORD_FONT_NAME);
437 appData.font = strdup(DEFAULT_FONT_NAME);
438 appData.icsFont = strdup(CONSOLE_FONT_NAME);
439 appData.tagsFont = strdup(TAGS_FONT_NAME);
440 appData.commentFont = strdup(COMMENT_FONT_NAME);
441 appData.historyFont = strdup(HISTORY_FONT_NAME);
442 appData.gameListFont = strdup(GAMELIST_FONT_NAME);
446 ChangeFont (int force, char **font, int fnr, int size, char *def, int pix)
448 if(!fontValid[fnr][size]) {
449 if(fontIsSet[fnr] && !force) return; // unless forced we do not replace an explicitly specified font by a default
450 ASSIGN(fontTable[fnr][size], def); // use default
451 fontIsSet[fnr] = False;
452 } else fontIsSet[fnr] = True;
453 FREE(*font); *font = InsertPxlSize(fontTable[fnr][size], pix);
458 { // no-op, until we identify the code for this already in XBoard and move it here
462 ParseColor (int n, char *name)
463 { // in XBoard, just copy the color-name string
464 if(colorVariable[n] && *name == '#') *(char**)colorVariable[n] = strdup(name);
470 return *(char**)colorVariable[n];
474 ParseTextAttribs (ColorClass cc, char *s)
476 (&appData.colorShout)[cc] = strdup(s);
480 ParseBoardSize (void *addr, char *name)
482 appData.boardSize = strdup(name);
487 { // In XBoard the sound-playing program takes care of obtaining the actual sound
491 SetCommPortDefaults ()
492 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
495 // [HGM] args: these three cases taken out to stay in front-end
497 SaveFontArg (FILE *f, ArgDescriptor *ad)
500 int i, n = (int)(intptr_t)ad->argLoc;
502 case 0: // CLOCK_FONT
503 name = appData.clockFont;
505 case 1: // MESSAGE_FONT
508 case 2: // COORD_FONT
509 name = appData.coordFont;
512 name = appData.icsFont;
515 name = appData.tagsFont;
518 name = appData.commentFont;
520 case MOVEHISTORY_FONT:
521 name = appData.historyFont;
524 name = appData.gameListFont;
529 if(fontIsSet[n]) // only save fonts that were not defaults
530 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
531 if(sizeDefaults[i].squareSize == initialSquareSize) { // only for standard sizes!
532 ASSIGN(fontTable[n][initialSquareSize], name);
533 fontValid[n][initialSquareSize] = True;
536 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
537 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
542 { // nothing to do, as the sounds are at all times represented by their text-string names already
546 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
547 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
548 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
552 SaveColor (FILE *f, ArgDescriptor *ad)
553 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
554 if(colorVariable[(int)(intptr_t)ad->argLoc])
555 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
559 SaveBoardSize (FILE *f, char *name, void *addr)
560 { // wrapper to shield back-end from BoardSize & sizeInfo
561 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
565 ParseCommPortSettings (char *s)
566 { // no such option in XBoard (yet)
572 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
576 gtk_widget_get_allocation(shell, &a);
577 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
581 wp->height = a.height;
582 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
583 frameX = 3; frameY = 3; // remember to decide if windows touch
587 GetPlacement (DialogClass dlg, WindowPlacement *wp)
588 { // wrapper to shield back-end from widget type
589 if(shellUp[dlg]) GetActualPlacement(shells[dlg], wp);
594 { // wrapper to shield use of window handles from back-end (make addressible by number?)
595 // In XBoard this will have to wait until awareness of window parameters is implemented
596 GetActualPlacement(shellWidget, &wpMain);
597 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
598 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
599 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
600 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
601 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
602 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
603 GetPlacement(ChatDlg, &wpConsole); if(appData.icsActive) wpConsole.visible = shellUp[ChatDlg];
607 PrintCommPortSettings (FILE *f, char *name)
608 { // This option does not exist in XBoard
612 EnsureOnScreen (int *x, int *y, int minX, int minY)
619 { // [HGM] args: allows testing if main window is realized from back-end
620 return DialogExists(BoardWindow);
624 PopUpStartupDialog ()
625 { // start menu not implemented in XBoard
629 ConvertToLine (int argc, char **argv)
631 static char line[128*1024], buf[1024];
635 for(i=1; i<argc; i++)
637 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
638 && argv[i][0] != '{' )
639 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
641 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
642 strncat(line, buf, 128*1024 - strlen(line) - 1 );
645 line[strlen(line)-1] = NULLCHAR;
649 //--------------------------------------------------------------------------------------------
654 ResizeBoardWindow (int w, int h, int inhibit)
658 // if(clockKludge) return; // ignore as long as clock does not have final height
659 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
661 gtk_widget_get_allocation(shellWidget, &a);
662 marginW = a.width - bw;
663 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
664 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
665 // w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
666 // h += marginH + a.height + 1;
667 gtk_window_resize(GTK_WINDOW(shellWidget), w, 10);
669 gtk_widget_set_size_request(optList[W_BOARD].handle, 100, 100); // liberate board again
674 { // dummy, as the GTK code does not make colors in advance
679 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
680 { // determine what fonts to use, and create them
682 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
683 appData.clockFont = fontTable[CLOCK_FONT][squareSize], fontIsSet[CLOCK_FONT] = True;
684 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
685 appData.font = fontTable[MESSAGE_FONT][squareSize], fontIsSet[MESSAGE_FONT] = True;
686 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
687 appData.coordFont = fontTable[COORD_FONT][squareSize], fontIsSet[COORD_FONT] = True;
688 if(!fontIsSet[CONSOLE_FONT] && fontValid[CONSOLE_FONT][squareSize])
689 appData.icsFont = fontTable[CONSOLE_FONT][squareSize], fontIsSet[CONSOLE_FONT] = True;
690 if(!fontIsSet[EDITTAGS_FONT] && fontValid[EDITTAGS_FONT][squareSize])
691 appData.tagsFont = fontTable[EDITTAGS_FONT][squareSize], fontIsSet[EDITTAGS_FONT] = True;
692 if(!fontIsSet[COMMENT_FONT] && fontValid[COMMENT_FONT][squareSize])
693 appData.commentFont = fontTable[COMMENT_FONT][squareSize], fontIsSet[COMMENT_FONT] = True;
694 if(!fontIsSet[MOVEHISTORY_FONT] && fontValid[MOVEHISTORY_FONT][squareSize])
695 appData.historyFont = fontTable[MOVEHISTORY_FONT][squareSize], fontIsSet[MOVEHISTORY_FONT] = True;
696 if(!fontIsSet[GAMELIST_FONT] && fontValid[GAMELIST_FONT][squareSize])
697 appData.gameListFont = fontTable[GAMELIST_FONT][squareSize], fontIsSet[GAMELIST_FONT] = True;
699 appData.font = InsertPxlSize(appData.font, coordFontPxlSize);
700 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
701 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
702 appData.icsFont = InsertPxlSize(appData.icsFont, coordFontPxlSize);
703 appData.tagsFont = InsertPxlSize(appData.tagsFont, coordFontPxlSize);
704 appData.commentFont = InsertPxlSize(appData.commentFont, coordFontPxlSize);
705 appData.historyFont = InsertPxlSize(appData.historyFont, coordFontPxlSize);
706 appData.gameListFont = InsertPxlSize(appData.gameListFont, coordFontPxlSize);
712 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
713 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
714 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
715 appData.font = fontTable[MESSAGE_FONT][squareSize];
716 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
717 appData.coordFont = fontTable[COORD_FONT][squareSize];
720 appData.font = InsertPxlSize(appData.font, fontPxlSize);
721 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
722 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
723 fontSet = CreateFontSet(appData.font);
724 clockFontSet = CreateFontSet(appData.clockFont);
726 /* For the coordFont, use the 0th font of the fontset. */
727 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
728 XFontStruct **font_struct_list;
729 XFontSetExtents *fontSize;
730 char **font_name_list;
731 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
732 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
733 coordFontStruct = XQueryFont(xDisplay, coordFontID);
734 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
735 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
738 appData.font = FindFont(appData.font, fontPxlSize);
739 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
740 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
741 clockFontID = XLoadFont(xDisplay, appData.clockFont);
742 clockFontStruct = XQueryFont(xDisplay, clockFontID);
743 coordFontID = XLoadFont(xDisplay, appData.coordFont);
744 coordFontStruct = XQueryFont(xDisplay, coordFontID);
745 // textHeight in !NLS mode!
747 countFontID = coordFontID; // [HGM] holdings
748 countFontStruct = coordFontStruct;
750 xdb = XtDatabase(xDisplay);
752 XrmPutLineResource(&xdb, "*international: True");
753 vTo.size = sizeof(XFontSet);
754 vTo.addr = (XtPointer) &fontSet;
755 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
757 XrmPutStringResource(&xdb, "*font", appData.font);
768 case ArgInt: p = " N"; break;
769 case ArgString: p = " STR"; break;
770 case ArgBoolean: p = " TF"; break;
771 case ArgSettingsFilename:
772 case ArgBackupSettingsFile:
773 case ArgFilename: p = " FILE"; break;
774 case ArgX: p = " Nx"; break;
775 case ArgY: p = " Ny"; break;
776 case ArgAttribs: p = " TEXTCOL"; break;
777 case ArgColor: p = " COL"; break;
778 case ArgFont: p = " FONT"; break;
779 case ArgBoardSize: p = " SIZE"; break;
780 case ArgFloat: p = " FLOAT"; break;
785 case ArgCommSettings:
797 ArgDescriptor *q, *p = argDescriptors+5;
798 printf("\nXBoard accepts the following options:\n"
799 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
800 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
801 " SIZE = board-size spec(s)\n"
802 " Within parentheses are short forms, or options to set to true or false.\n"
803 " Persistent options (saved in the settings file) are marked with *)\n\n");
805 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
806 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
807 if(p->save) strcat(buf+len, "*");
808 for(q=p+1; q->argLoc == p->argLoc; q++) {
809 if(q->argName[0] == '-') continue;
810 strcat(buf+len, q == p+1 ? " (" : " ");
811 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
813 if(q != p+1) strcat(buf+len, ")");
815 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
818 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
822 SlaveResize (Option *opt)
824 static int slaveW, slaveH, w, h;
827 gtk_widget_get_allocation(shells[DummyDlg], &a);
828 w = a.width; h = a.height;
829 gtk_widget_get_allocation(opt->handle, &a);
830 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
831 slaveH = h - a.height + 13;
833 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
837 LoadIconFile (gchar *svgFilename)
841 snprintf(buf, MSG_SIZ, "%s/%s" IMG, svgDir, svgFilename);
842 return gdk_pixbuf_new_from_file(buf, NULL);
846 static char clickedFile[MSG_SIZ];
850 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
851 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
854 if(1000*now.sec + now.ms - 1000*started.sec - started.ms < 1000) { // received during first second
855 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
856 } else { // we are running something presumably useful
858 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
859 system(buf); // start new instance on this file
864 GtkosxApplication *theApp;
868 main (int argc, char **argv)
870 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
871 int boardWidth, w, h; //, boardHeight;
873 int forceMono = False;
875 srandom(time(0)); // [HGM] book: make random truly random
877 setbuf(stdout, NULL);
878 setbuf(stderr, NULL);
881 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
882 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
886 if(argc > 1 && !strcmp(argv[1], "--help" )) {
892 gtk_init (&argc, &argv);
894 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
895 char *path = gtkosx_application_get_bundle_path();
897 char *res_path = gtkosx_application_get_resource_path();
898 snprintf(localeDir, MSG_SIZ, "%s/share/locale", res_path); // redefine locale dir for OSX bundle
900 GetTimeMark(&started); // remember start time
901 theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
902 snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
903 snprintf(dataDir, MSG_SIZ, "%s/Contents/Resources/share/xboard", path);
904 snprintf(manDir, MSG_SIZ, "%s/Contents/Resources/share/man", path);
905 snprintf(svgDir, MSG_SIZ, "%s/themes/default", dataDir);
906 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
907 g_signal_connect(theApp, "NSApplicationWillTerminate", G_CALLBACK(ExitEvent), NULL);
908 // we must call application ready before we can get the signal,
909 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
910 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
911 gtkosx_application_ready(theApp);
912 if(argc == 1) { // called without args: OSX open-file signal might follow
913 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
914 usleep(10000); // wait 10 msec (and hope this is long enough).
915 while(gtk_events_pending())
916 gtk_main_iteration(); // process all events that came in upto now
917 if(clickedFile[0]) { // we were sent an open-file signal with filename!
918 fakeArgv[0] = argv[0];
919 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
925 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
926 typedef struct {char *name, *value; } Config;
927 static Config configList[] = {
928 { "Datadir", dataDir },
929 { "Mandir", manDir },
930 { "Sysconfdir", SYSCONFDIR },
935 for(i=0; configList[i].name; i++) {
936 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
937 if(argc > 2) printf("%s", configList[i].value);
938 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
943 /* set up keyboard accelerators group */
944 GtkAccelerators = gtk_accel_group_new();
946 programName = strrchr(argv[0], '/');
947 if (programName == NULL)
948 programName = argv[0];
953 // if (appData.debugMode) {
954 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
957 bindtextdomain(PACKAGE, LOCALEDIR);
958 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
962 appData.boardSize = "";
963 InitAppData(ConvertToLine(argc, argv));
965 if (p == NULL) p = "/tmp";
966 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
967 gameCopyFilename = (char*) malloc(i);
968 gamePasteFilename = (char*) malloc(i);
969 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
970 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
972 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
973 static char buf[MSG_SIZ];
974 snprintf(buf, MSG_SIZ, appData.sysOpen, dataDir);
975 ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
976 EscapeExpand(buf, appData.firstInitString);
977 appData.firstInitString = strdup(buf);
978 EscapeExpand(buf, appData.secondInitString);
979 appData.secondInitString = strdup(buf);
980 EscapeExpand(buf, appData.firstComputerString);
981 appData.firstComputerString = strdup(buf);
982 EscapeExpand(buf, appData.secondComputerString);
983 appData.secondComputerString = strdup(buf);
986 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
989 if (chdir(chessDir) != 0) {
990 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
996 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
997 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
998 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
999 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1002 setbuf(debugFP, NULL);
1006 if (appData.debugMode) {
1007 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1011 /* [HGM,HR] make sure board size is acceptable */
1012 if(appData.NrFiles > BOARD_FILES ||
1013 appData.NrRanks > BOARD_RANKS )
1014 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1017 /* This feature does not work; animation needs a rewrite */
1018 appData.highlightDragging = FALSE;
1022 gameInfo.variant = StringToVariant(appData.variant);
1023 InitPosition(FALSE);
1026 * determine size, based on supplied or remembered -size, or screen size
1028 if (isdigit(appData.boardSize[0])) {
1029 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1030 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1031 &fontPxlSize, &smallLayout, &tinyLayout);
1033 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1034 programName, appData.boardSize);
1038 /* Find some defaults; use the nearest known size */
1039 SizeDefaults *szd, *nearest;
1040 int distance = 99999;
1041 nearest = szd = sizeDefaults;
1042 while (szd->name != NULL) {
1043 if (abs(szd->squareSize - squareSize) < distance) {
1045 distance = abs(szd->squareSize - squareSize);
1046 if (distance == 0) break;
1050 if (i < 2) lineGap = nearest->lineGap;
1051 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1052 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1053 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1054 if (i < 6) smallLayout = nearest->smallLayout;
1055 if (i < 7) tinyLayout = nearest->tinyLayout;
1058 SizeDefaults *szd = sizeDefaults;
1059 if (*appData.boardSize == NULLCHAR) {
1060 // GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow)); // TODO: this does not work, as no mainwindow yet
1061 GdkScreen *screen = gdk_screen_get_default();
1062 guint screenwidth = gdk_screen_get_width(screen);
1063 guint screenheight = gdk_screen_get_height(screen);
1064 while (screenwidth < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1065 screenheight < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1068 if (szd->name == NULL) szd--;
1069 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1071 while (szd->name != NULL &&
1072 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1073 if (szd->name == NULL) {
1074 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1075 programName, appData.boardSize);
1079 squareSize = szd->squareSize;
1080 lineGap = szd->lineGap;
1081 clockFontPxlSize = szd->clockFontPxlSize;
1082 coordFontPxlSize = szd->coordFontPxlSize;
1083 fontPxlSize = szd->fontPxlSize;
1084 smallLayout = szd->smallLayout;
1085 tinyLayout = szd->tinyLayout;
1086 // [HGM] font: use defaults from settings file if available and not overruled
1088 initialSquareSize = squareSize; // [HGM] remember for saving font info
1089 if(BOARD_WIDTH != 8) {
1090 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // keep width the same
1091 lineGap = (squareSize < 37 ? 1 : squareSize < 59 ? 2 : squareSize < 116 ? 3 : 4);
1094 defaultLineGap = lineGap;
1095 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1097 /* [HR] height treated separately (hacked) */
1098 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1099 // boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1102 * Determine what fonts to use.
1104 InitializeFonts((2*clockFontPxlSize+1)/3, coordFontPxlSize, fontPxlSize);
1107 * Detect if there are not enough colors available and adapt.
1110 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1111 appData.monoMode = True;
1115 forceMono = MakeColors();
1118 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1120 appData.monoMode = True;
1123 ParseIcsTextColors();
1129 layoutName = "tinyLayout";
1130 } else if (smallLayout) {
1131 layoutName = "smallLayout";
1133 layoutName = "normalLayout";
1136 if(appData.logoSize) appData.logoSize = boardWidth/4-3;
1137 wpMain.width = -1; // prevent popup sizes window
1138 optList = BoardPopUp(squareSize, lineGap, (void*)
1148 InitDrawingHandle(optList + W_BOARD);
1149 shellWidget = shells[BoardWindow];
1150 currBoard = &optList[W_BOARD];
1151 boardWidget = optList[W_BOARD].handle;
1152 menuBarWidget = optList[W_MENU].handle;
1153 dropMenu = optList[W_DROP].handle;
1154 titleWidget = optList[optList[W_TITLE].type != Skip ? W_TITLE : W_SMALL].handle;
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+1].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.historyFont, MOVEHISTORY_FONT, initialSquareSize, HISTORY_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
1714 DisplayBothClocks();
1715 ApplyFont(&mainOptions[W_MESSG], NULL);
1716 for(i=1; i<6; i++) ApplyFont(&mainOptions[W_BUTTON+i], NULL);
1717 ApplyFont(&tagsOptions[1], NULL);
1718 ApplyFont(&commentOptions[0], NULL);
1719 ApplyFont(&historyOptions[0], NULL);
1720 ApplyFont(&engoutOptions[5], NULL);
1721 ApplyFont(&engoutOptions[12], NULL);
1722 ApplyFont(&chatOptions[11], appData.icsFont);
1723 AppendColorized(&chatOptions[6], NULL, 0); // kludge to replace font tag
1725 if(!strchr(appData.boardSize, ',')) {
1726 ASSIGN(appData.boardSize, sizeDefaults[h].name);
1729 if(sizeDefaults[h].tinyLayout != tinyLayout) { // alter clipping of menu names to conform to board width
1730 int clip = (tinyLayout = sizeDefaults[h].tinyLayout) + 1;
1732 for(h=1; mainOptions[h].type == DropDown; h++) {
1733 strncpy(text, _(mainOptions[h].name), MSG_SIZ);
1734 if(clip != 1) text[clip + (text[clip-1] == '_')] = NULLCHAR;
1735 gtk_menu_item_set_label((GtkMenuItem *) mainOptions[h].handle, text);
1739 if(sqx != squareSize && !first) {
1740 squareSize = sqx; // adopt new square size
1741 CreatePNGPieces(); // make newly scaled pieces
1742 InitDrawingSizes(0, 0); // creates grid etc.
1743 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1744 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1745 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1746 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1747 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1748 first = appData.fixedSize;
1749 if(twoBoards && shellUp[DummyDlg]) {
1750 SlavePopUp(); dualOptions[3].max = 0; DoEvents(); // calls SlaveResize, kludge to force assigning new canvas
1751 partnerUp = !partnerUp; flipView = !flipView;
1752 DrawPosition(True, NULL);
1753 partnerUp = !partnerUp; flipView = !flipView;
1757 static guint delayedDragTag = 0;
1763 if(busy++) return; // prevent recursive calling, but remember we missed an event in 'busy'
1765 if(delayedDragTag) g_source_remove(delayedDragTag); // no more timer interrupts from same event!
1769 GetActualPlacement(shellWidget, &wpNew);
1770 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1771 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1772 busy = 0; break; // false alarm
1774 ReSize(&wpNew); // this can be interrupted by other events
1775 if(appData.useStickyWindows) {
1776 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1777 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1778 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1779 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1780 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1783 DrawPosition(True, NULL);
1784 if(busy > 2) busy = 2; // if multiple events were backlogged, only do one more
1791 //printf("old timr = %d\n", delayedDragTag);
1792 if(delayedDragTag) g_source_remove(delayedDragTag);
1793 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1794 //printf("new timr = %d\n", delayedDragTag);
1798 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1800 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1802 wpNew.x = event->configure.x;
1803 wpNew.y = event->configure.y;
1804 wpNew.width = event->configure.width;
1805 wpNew.height = event->configure.height;
1806 // SetWidgetLabel(&mainOptions[W_WHITE], ""); SetWidgetLabel(&mainOptions[W_BLACK], "");
1807 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1813 /* Disable all user input other than deleting the window */
1814 static int frozen = 0;
1820 /* Grab by a widget that doesn't accept input */
1821 gtk_grab_add(optList[W_MESSG].handle);
1825 /* Undo a FreezeUI */
1829 if (!frozen) return;
1830 gtk_grab_remove(optList[W_MESSG].handle);
1837 static int oldPausing = FALSE;
1838 static GameMode oldMode = (GameMode) -1;
1840 if (!boardWidget) return;
1842 if (pausing != oldPausing) {
1843 oldPausing = pausing;
1844 MarkMenuItem("Mode.Pause", pausing);
1846 if (appData.showButtonBar) {
1847 /* Always toggle, don't set. Previous code messes up when
1848 invoked while the button is pressed, as releasing it
1849 toggles the state again. */
1851 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1852 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1856 wname = ModeToWidgetName(oldMode);
1857 if (wname != NULL) {
1858 MarkMenuItem(wname, False);
1860 wname = ModeToWidgetName(gameMode);
1861 if (wname != NULL) {
1862 MarkMenuItem(wname, True);
1864 if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
1865 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1868 /* Maybe all the enables should be handled here, not just this one */
1869 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1871 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1876 * Button/menu procedures
1879 void CopyFileToClipboard(gchar *filename)
1881 gchar *selection_tmp;
1885 FILE* f = fopen(filename, "r");
1888 if (f == NULL) return;
1892 selection_tmp = g_try_malloc(len + 1);
1893 if (selection_tmp == NULL) {
1894 printf("Malloc failed in CopyFileToClipboard\n");
1897 count = fread(selection_tmp, 1, len, f);
1900 g_free(selection_tmp);
1903 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1905 // copy selection_tmp to clipboard
1906 GdkDisplay *gdisp = gdk_display_get_default();
1908 g_free(selection_tmp);
1911 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1912 gtk_clipboard_set_text(cb, selection_tmp, -1);
1913 g_free(selection_tmp);
1917 CopySomething (char *src)
1919 GdkDisplay *gdisp = gdk_display_get_default();
1921 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1922 if (gdisp == NULL) return;
1923 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1924 gtk_clipboard_set_text(cb, src, -1);
1928 PastePositionProc ()
1930 GdkDisplay *gdisp = gdk_display_get_default();
1934 if (gdisp == NULL) return;
1935 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1936 fenstr = gtk_clipboard_wait_for_text(cb);
1937 if (fenstr==NULL) return; // nothing had been selected to copy
1938 EditPositionPasteFEN(fenstr);
1947 guint len=0; int flip = appData.flipView;
1950 // get game from clipboard
1951 GdkDisplay *gdisp = gdk_display_get_default();
1952 if (gdisp == NULL) return;
1953 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1954 text = gtk_clipboard_wait_for_text(cb);
1955 if (text == NULL) return; // nothing to paste
1958 // write to temp file
1959 if (text == NULL || len == 0) {
1960 return; //nothing to paste
1962 f = fopen(gamePasteFilename, "w");
1964 DisplayError(_("Can't open temp file"), errno);
1967 fwrite(text, 1, len, f);
1971 if(!appData.autoFlipView) appData.flipView = flipView;
1972 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1973 appData.flipView = flip;
1980 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1986 void MoveTypeInProc(eventkey)
1987 GdkEventKey *eventkey;
1991 // ingnore if ctrl, alt, or meta is pressed
1992 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1996 buf[0]=eventkey->keyval;
1998 if (eventkey->keyval > 32 && eventkey->keyval < 256 || *buf == 27)
1999 ConsoleAutoPopUp (buf);
2004 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2006 if (!TempBackwardActive) {
2007 TempBackwardActive = True;
2013 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2015 /* Check to see if triggered by a key release event for a repeating key.
2016 * If so the next queued event will be a key press of the same key at the same time */
2017 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2019 XPeekEvent(xDisplay, &next);
2020 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2021 next.xkey.keycode == event->xkey.keycode)
2025 TempBackwardActive = False;
2031 { // called from menu
2034 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"man %s/../man/man6/xboard.6\"' -e 'end tell'", dataDir);
2037 system("xterm -e man xboard &");
2046 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"info -d %s/../info -f xboard.info\"' -e 'end tell'", dataDir);
2048 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
2056 SetWindowTitle (char *text, char *title, char *icon)
2061 if (appData.titleInWindow) {
2063 XtSetArg(args[i], XtNlabel, text); i++;
2064 XtSetValues(titleWidget, args, i);
2067 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2068 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2069 XtSetValues(shellWidget, args, i);
2070 XSync(xDisplay, False);
2072 if (appData.titleInWindow) {
2073 SetWidgetLabel(titleWidget, text);
2075 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2080 DisplayIcsInteractionTitle (String message)
2083 if (oldICSInteractionTitle == NULL) {
2084 /* Magic to find the old window title, adapted from vim */
2085 char *wina = getenv("WINDOWID");
2087 Window win = (Window) atoi(wina);
2088 Window root, parent, *children;
2089 unsigned int nchildren;
2090 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2092 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2093 if (!XQueryTree(xDisplay, win, &root, &parent,
2094 &children, &nchildren)) break;
2095 if (children) XFree((void *)children);
2096 if (parent == root || parent == 0) break;
2099 XSetErrorHandler(oldHandler);
2101 if (oldICSInteractionTitle == NULL) {
2102 oldICSInteractionTitle = "xterm";
2105 printf("\033]0;%s\007", message);
2111 LockBoardSize (int after)
2113 static char *oldClockFont, *oldMessgFont;
2115 if(oldMessgFont && !strcmp(oldMessgFont, appData.font) &&
2116 oldClockFont && !strcmp(oldClockFont, appData.clockFont) && after < 2) return; // only do something when font changed
2117 w = BOARD_WIDTH*(squareSize + lineGap) + lineGap;
2118 h = BOARD_HEIGHT*(squareSize + lineGap) + lineGap;
2120 ASSIGN(oldClockFont, appData.clockFont);
2121 ASSIGN(oldMessgFont, appData.font);
2122 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
2124 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board
2126 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
2131 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2133 static int twoLines = -1;
2134 GtkWidget *w = (GtkWidget *) opt->handle;
2136 char *markup, two = (appData.logoSize != 0);
2141 strcpy(bgcolor, "black");
2142 strcpy(fgcolor, "white");
2144 strcpy(bgcolor, "white");
2145 strcpy(fgcolor, "black");
2148 appData.lowTimeWarning &&
2149 (timer / 1000) < appData.icsAlarmTime) {
2150 strcpy(fgcolor, appData.lowTimeWarningColor);
2153 if(! partnerUp && two != twoLines) LockBoardSize(2); // lock board size if clock height changes
2155 gdk_color_parse( bgcolor, &col );
2156 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2158 if (appData.clockMode) {
2159 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2160 bgcolor, fgcolor, color, two ? "\n" : " ", TimeString(timer));
2161 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2162 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2164 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2165 bgcolor, fgcolor, color);
2166 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2167 // bgcolor, fgcolor, color);
2169 gtk_label_set_markup(GTK_LABEL(w), markup);
2172 if(!partnerUp && two != twoLines) LockBoardSize(3), twoLines = two;
2175 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2178 SetClockIcon (int color)
2180 GdkPixbuf *pm = *clockIcons[color];
2181 if (mainwindowIcon != pm) {
2182 mainwindowIcon = pm;
2184 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2186 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2191 #define INPUT_SOURCE_BUF_SIZE 8192
2200 char buf[INPUT_SOURCE_BUF_SIZE];
2205 DoInputCallback(io, cond, data)
2210 /* read input from one of the input source (for example a chess program, ICS, etc).
2211 * and call a function that will handle the input
2218 /* All information (callback function, file descriptor, etc) is
2219 * saved in an InputSource structure
2221 InputSource *is = (InputSource *) data;
2223 if (is->lineByLine) {
2224 count = read(is->fd, is->unused,
2225 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2227 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2228 RemoveInputSource(is); // cease reading stdin
2229 stdoutClosed = TRUE; // suppress future output
2232 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2235 is->unused += count;
2237 /* break input into lines and call the callback function on each
2240 while (p < is->unused) {
2241 q = memchr(p, '\n', is->unused - p);
2242 if (q == NULL) break;
2244 (is->func)(is, is->closure, p, q - p, 0);
2247 /* remember not yet used part of the buffer */
2249 while (p < is->unused) {
2254 /* read maximum length of input buffer and send the whole buffer
2255 * to the callback function
2257 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2262 (is->func)(is, is->closure, is->buf, count, error);
2264 return True; // Must return true or the watch will be removed
2267 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2274 GIOChannel *channel;
2275 ChildProc *cp = (ChildProc *) pr;
2277 is = (InputSource *) calloc(1, sizeof(InputSource));
2278 is->lineByLine = lineByLine;
2282 is->fd = fileno(stdin);
2284 is->kind = cp->kind;
2285 is->fd = cp->fdFrom;
2288 is->unused = is->buf;
2292 /* GTK-TODO: will this work on windows?*/
2294 channel = g_io_channel_unix_new(is->fd);
2295 g_io_channel_set_close_on_unref (channel, TRUE);
2296 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2298 is->closure = closure;
2299 return (InputSourceRef) is;
2304 RemoveInputSource(isr)
2307 InputSource *is = (InputSource *) isr;
2309 if (is->sid == 0) return;
2310 g_source_remove(is->sid);
2317 static Boolean frameWaiting;
2320 FrameAlarm (int sig)
2322 frameWaiting = False;
2323 /* In case System-V style signals. Needed?? */
2324 signal(SIGALRM, FrameAlarm);
2328 FrameDelay (int time)
2330 struct itimerval delay;
2333 frameWaiting = True;
2334 signal(SIGALRM, FrameAlarm);
2335 delay.it_interval.tv_sec =
2336 delay.it_value.tv_sec = time / 1000;
2337 delay.it_interval.tv_usec =
2338 delay.it_value.tv_usec = (time % 1000) * 1000;
2339 setitimer(ITIMER_REAL, &delay, NULL);
2340 while (frameWaiting) pause();
2341 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2342 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2343 setitimer(ITIMER_REAL, &delay, NULL);
2350 FrameDelay (int time)
2353 XSync(xDisplay, False);
2355 // gtk_main_iteration_do(False);
2358 usleep(time * 1000);
2364 FindLogo (char *place, char *name, char *buf)
2365 { // check if file exists in given place
2367 if(!place) return 0;
2368 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2369 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2377 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2379 char buf[MSG_SIZ], *logoName = buf;
2380 if(appData.logo[n][0]) {
2381 logoName = appData.logo[n];
2382 } else if(appData.autoLogo) {
2383 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2384 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2385 } else { // engine; cascade
2386 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2387 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2388 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2389 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2393 { ASSIGN(cps->programLogo, logoName); }
2397 UpdateLogos (int displ)
2399 if(optList[W_WHITE-1].handle == NULL) return;
2400 LoadLogo(&first, 0, 0);
2401 LoadLogo(&second, 1, appData.icsActive);
2402 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2406 void FileNamePopUpWrapper(
2417 GtkFileFilter *gtkfilter;
2418 GtkFileFilter *gtkfilter_all;
2420 char fileext[10] = "";
2421 char *result = NULL;
2423 char curDir[MSG_SIZ];
2425 StartDir(filter, NULL); // change to start directory for this file type
2427 if(def && *def && def[strlen(def)-1] == '/') {
2428 getcwd(curDir, MSG_SIZ);
2433 /* make a copy of the filter string, so that strtok can work with it*/
2434 cp = strdup(filter);
2436 /* add filters for file extensions */
2437 gtkfilter = gtk_file_filter_new();
2438 gtkfilter_all = gtk_file_filter_new();
2440 /* one filter to show everything */
2441 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2442 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2444 /* add filter if present */
2445 result = strtok(cp, space);
2446 while( result != NULL ) {
2447 snprintf(fileext,10,"*%s",result);
2448 result = strtok( NULL, space );
2449 gtk_file_filter_add_pattern(gtkfilter, fileext);
2452 /* second filter to only show what's useful */
2453 gtk_file_filter_set_name (gtkfilter,filter);
2455 if (openMode[0] == 'r')
2457 dialog = gtk_file_chooser_dialog_new (label,
2459 GTK_FILE_CHOOSER_ACTION_OPEN,
2460 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2461 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2466 dialog = gtk_file_chooser_dialog_new (label,
2468 GTK_FILE_CHOOSER_ACTION_SAVE,
2469 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2470 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2472 /* add filename suggestions */
2473 if (strlen(def) > 0 )
2474 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2476 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2480 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2481 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2482 /* activate filter */
2483 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2485 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2490 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2493 f = fopen(filename, openMode);
2496 DisplayError(_("Failed to open file"), errno);
2500 /* TODO add indec */
2502 ASSIGN(*name, filename);
2503 ScheduleDelayedEvent(DelayedLoad, 50);
2505 StartDir(filter, filename);
2508 else StartDir(filter, "");
2510 gtk_widget_destroy (dialog);
2513 if(def && *def && def[strlen(def)-1] == '/') chdir(curDir);