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);
448 { // no-op, until we identify the code for this already in XBoard and move it here
452 ParseColor (int n, char *name)
453 { // in XBoard, just copy the color-name string
454 if(colorVariable[n] && *name == '#') *(char**)colorVariable[n] = strdup(name);
460 return *(char**)colorVariable[n];
464 ParseTextAttribs (ColorClass cc, char *s)
466 (&appData.colorShout)[cc] = strdup(s);
470 ParseBoardSize (void *addr, char *name)
472 appData.boardSize = strdup(name);
477 { // In XBoard the sound-playing program takes care of obtaining the actual sound
481 SetCommPortDefaults ()
482 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
485 // [HGM] args: these three cases taken out to stay in front-end
487 SaveFontArg (FILE *f, ArgDescriptor *ad)
490 int i, n = (int)(intptr_t)ad->argLoc;
492 case 0: // CLOCK_FONT
493 name = appData.clockFont;
495 case 1: // MESSAGE_FONT
498 case 2: // COORD_FONT
499 name = appData.coordFont;
502 name = appData.icsFont;
505 name = appData.tagsFont;
508 name = appData.commentFont;
510 case MOVEHISTORY_FONT:
511 name = appData.historyFont;
514 name = appData.gameListFont;
519 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
520 if(sizeDefaults[i].squareSize == initialSquareSize) { // only for standard sizes!
521 fontTable[n][initialSquareSize] = strdup(name);
522 fontValid[n][initialSquareSize] = True;
525 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
526 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
531 { // nothing to do, as the sounds are at all times represented by their text-string names already
535 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
536 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
537 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
541 SaveColor (FILE *f, ArgDescriptor *ad)
542 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
543 if(colorVariable[(int)(intptr_t)ad->argLoc])
544 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
548 SaveBoardSize (FILE *f, char *name, void *addr)
549 { // wrapper to shield back-end from BoardSize & sizeInfo
550 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
554 ParseCommPortSettings (char *s)
555 { // no such option in XBoard (yet)
561 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
565 gtk_widget_get_allocation(shell, &a);
566 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
570 wp->height = a.height;
571 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
572 frameX = 3; frameY = 3; // remember to decide if windows touch
576 GetPlacement (DialogClass dlg, WindowPlacement *wp)
577 { // wrapper to shield back-end from widget type
578 if(shellUp[dlg]) GetActualPlacement(shells[dlg], wp);
583 { // wrapper to shield use of window handles from back-end (make addressible by number?)
584 // In XBoard this will have to wait until awareness of window parameters is implemented
585 GetActualPlacement(shellWidget, &wpMain);
586 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
587 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
588 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
589 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
590 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
591 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
592 GetPlacement(ChatDlg, &wpConsole); if(appData.icsActive) wpConsole.visible = shellUp[ChatDlg];
596 PrintCommPortSettings (FILE *f, char *name)
597 { // This option does not exist in XBoard
601 EnsureOnScreen (int *x, int *y, int minX, int minY)
608 { // [HGM] args: allows testing if main window is realized from back-end
609 return DialogExists(BoardWindow);
613 PopUpStartupDialog ()
614 { // start menu not implemented in XBoard
618 ConvertToLine (int argc, char **argv)
620 static char line[128*1024], buf[1024];
624 for(i=1; i<argc; i++)
626 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
627 && argv[i][0] != '{' )
628 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
630 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
631 strncat(line, buf, 128*1024 - strlen(line) - 1 );
634 line[strlen(line)-1] = NULLCHAR;
638 //--------------------------------------------------------------------------------------------
643 ResizeBoardWindow (int w, int h, int inhibit)
647 // if(clockKludge) return; // ignore as long as clock does not have final height
648 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
650 gtk_widget_get_allocation(shellWidget, &a);
651 marginW = a.width - bw;
652 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
653 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
654 // w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
655 // h += marginH + a.height + 1;
656 gtk_window_resize(GTK_WINDOW(shellWidget), w, 10);
658 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board again
663 { // dummy, as the GTK code does not make colors in advance
668 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
669 { // determine what fonts to use, and create them
671 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
672 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
673 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
674 appData.font = fontTable[MESSAGE_FONT][squareSize];
675 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
676 appData.coordFont = fontTable[COORD_FONT][squareSize];
677 if(!fontIsSet[CONSOLE_FONT] && fontValid[CONSOLE_FONT][squareSize])
678 appData.icsFont = fontTable[CONSOLE_FONT][squareSize];
679 if(!fontIsSet[EDITTAGS_FONT] && fontValid[EDITTAGS_FONT][squareSize])
680 appData.tagsFont = fontTable[EDITTAGS_FONT][squareSize];
681 if(!fontIsSet[COMMENT_FONT] && fontValid[COMMENT_FONT][squareSize])
682 appData.commentFont = fontTable[COMMENT_FONT][squareSize];
683 if(!fontIsSet[MOVEHISTORY_FONT] && fontValid[MOVEHISTORY_FONT][squareSize])
684 appData.historyFont = fontTable[MOVEHISTORY_FONT][squareSize];
685 if(!fontIsSet[GAMELIST_FONT] && fontValid[GAMELIST_FONT][squareSize])
686 appData.gameListFont = fontTable[GAMELIST_FONT][squareSize];
688 appData.font = InsertPxlSize(appData.font, coordFontPxlSize);
689 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
690 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
691 appData.icsFont = InsertPxlSize(appData.icsFont, coordFontPxlSize);
692 appData.tagsFont = InsertPxlSize(appData.tagsFont, coordFontPxlSize);
693 appData.commentFont = InsertPxlSize(appData.commentFont, coordFontPxlSize);
694 appData.historyFont = InsertPxlSize(appData.historyFont, coordFontPxlSize);
695 appData.gameListFont = InsertPxlSize(appData.gameListFont, coordFontPxlSize);
701 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
702 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
703 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
704 appData.font = fontTable[MESSAGE_FONT][squareSize];
705 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
706 appData.coordFont = fontTable[COORD_FONT][squareSize];
709 appData.font = InsertPxlSize(appData.font, fontPxlSize);
710 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
711 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
712 fontSet = CreateFontSet(appData.font);
713 clockFontSet = CreateFontSet(appData.clockFont);
715 /* For the coordFont, use the 0th font of the fontset. */
716 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
717 XFontStruct **font_struct_list;
718 XFontSetExtents *fontSize;
719 char **font_name_list;
720 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
721 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
722 coordFontStruct = XQueryFont(xDisplay, coordFontID);
723 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
724 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
727 appData.font = FindFont(appData.font, fontPxlSize);
728 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
729 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
730 clockFontID = XLoadFont(xDisplay, appData.clockFont);
731 clockFontStruct = XQueryFont(xDisplay, clockFontID);
732 coordFontID = XLoadFont(xDisplay, appData.coordFont);
733 coordFontStruct = XQueryFont(xDisplay, coordFontID);
734 // textHeight in !NLS mode!
736 countFontID = coordFontID; // [HGM] holdings
737 countFontStruct = coordFontStruct;
739 xdb = XtDatabase(xDisplay);
741 XrmPutLineResource(&xdb, "*international: True");
742 vTo.size = sizeof(XFontSet);
743 vTo.addr = (XtPointer) &fontSet;
744 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
746 XrmPutStringResource(&xdb, "*font", appData.font);
757 case ArgInt: p = " N"; break;
758 case ArgString: p = " STR"; break;
759 case ArgBoolean: p = " TF"; break;
760 case ArgSettingsFilename:
761 case ArgBackupSettingsFile:
762 case ArgFilename: p = " FILE"; break;
763 case ArgX: p = " Nx"; break;
764 case ArgY: p = " Ny"; break;
765 case ArgAttribs: p = " TEXTCOL"; break;
766 case ArgColor: p = " COL"; break;
767 case ArgFont: p = " FONT"; break;
768 case ArgBoardSize: p = " SIZE"; break;
769 case ArgFloat: p = " FLOAT"; break;
774 case ArgCommSettings:
786 ArgDescriptor *q, *p = argDescriptors+5;
787 printf("\nXBoard accepts the following options:\n"
788 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
789 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
790 " SIZE = board-size spec(s)\n"
791 " Within parentheses are short forms, or options to set to true or false.\n"
792 " Persistent options (saved in the settings file) are marked with *)\n\n");
794 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
795 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
796 if(p->save) strcat(buf+len, "*");
797 for(q=p+1; q->argLoc == p->argLoc; q++) {
798 if(q->argName[0] == '-') continue;
799 strcat(buf+len, q == p+1 ? " (" : " ");
800 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
802 if(q != p+1) strcat(buf+len, ")");
804 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
807 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
811 SlaveResize (Option *opt)
813 static int slaveW, slaveH, w, h;
816 gtk_widget_get_allocation(shells[DummyDlg], &a);
817 w = a.width; h = a.height;
818 gtk_widget_get_allocation(opt->handle, &a);
819 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
820 slaveH = h - a.height + 13;
822 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
826 LoadIconFile (gchar *svgFilename)
830 snprintf(buf, MSG_SIZ, "%s/%s" IMG, svgDir, svgFilename);
831 return gdk_pixbuf_new_from_file(buf, NULL);
835 static char clickedFile[MSG_SIZ];
839 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
840 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
843 if(1000*now.sec + now.ms - 1000*started.sec - started.ms < 1000) { // received during first second
844 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
845 } else { // we are running something presumably useful
847 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
848 system(buf); // start new instance on this file
853 GtkosxApplication *theApp;
857 main (int argc, char **argv)
859 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
860 int boardWidth, w, h; //, boardHeight;
862 int forceMono = False;
864 srandom(time(0)); // [HGM] book: make random truly random
866 setbuf(stdout, NULL);
867 setbuf(stderr, NULL);
870 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
871 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
875 if(argc > 1 && !strcmp(argv[1], "--help" )) {
881 gtk_init (&argc, &argv);
883 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
884 char *path = gtkosx_application_get_bundle_path();
886 char *res_path = gtkosx_application_get_resource_path();
887 snprintf(localeDir, MSG_SIZ, "%s/share/locale", res_path); // redefine locale dir for OSX bundle
889 GetTimeMark(&started); // remember start time
890 theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
891 snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
892 snprintf(dataDir, MSG_SIZ, "%s/Contents/Resources/share/xboard", path);
893 snprintf(manDir, MSG_SIZ, "%s/Contents/Resources/share/man", path);
894 snprintf(svgDir, MSG_SIZ, "%s/themes/default", dataDir);
895 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
896 g_signal_connect(theApp, "NSApplicationWillTerminate", G_CALLBACK(ExitEvent), NULL);
897 // we must call application ready before we can get the signal,
898 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
899 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
900 gtkosx_application_ready(theApp);
901 if(argc == 1) { // called without args: OSX open-file signal might follow
902 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
903 usleep(10000); // wait 10 msec (and hope this is long enough).
904 while(gtk_events_pending())
905 gtk_main_iteration(); // process all events that came in upto now
906 if(clickedFile[0]) { // we were sent an open-file signal with filename!
907 fakeArgv[0] = argv[0];
908 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
914 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
915 typedef struct {char *name, *value; } Config;
916 static Config configList[] = {
917 { "Datadir", dataDir },
918 { "Mandir", manDir },
919 { "Sysconfdir", SYSCONFDIR },
924 for(i=0; configList[i].name; i++) {
925 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
926 if(argc > 2) printf("%s", configList[i].value);
927 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
932 /* set up keyboard accelerators group */
933 GtkAccelerators = gtk_accel_group_new();
935 programName = strrchr(argv[0], '/');
936 if (programName == NULL)
937 programName = argv[0];
942 // if (appData.debugMode) {
943 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
946 bindtextdomain(PACKAGE, LOCALEDIR);
947 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
951 appData.boardSize = "";
952 InitAppData(ConvertToLine(argc, argv));
954 if (p == NULL) p = "/tmp";
955 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
956 gameCopyFilename = (char*) malloc(i);
957 gamePasteFilename = (char*) malloc(i);
958 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
959 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
961 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
962 static char buf[MSG_SIZ];
963 snprintf(buf, MSG_SIZ, appData.sysOpen, dataDir);
964 ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
965 EscapeExpand(buf, appData.firstInitString);
966 appData.firstInitString = strdup(buf);
967 EscapeExpand(buf, appData.secondInitString);
968 appData.secondInitString = strdup(buf);
969 EscapeExpand(buf, appData.firstComputerString);
970 appData.firstComputerString = strdup(buf);
971 EscapeExpand(buf, appData.secondComputerString);
972 appData.secondComputerString = strdup(buf);
975 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
978 if (chdir(chessDir) != 0) {
979 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
985 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
986 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
987 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
988 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
991 setbuf(debugFP, NULL);
995 if (appData.debugMode) {
996 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1000 /* [HGM,HR] make sure board size is acceptable */
1001 if(appData.NrFiles > BOARD_FILES ||
1002 appData.NrRanks > BOARD_RANKS )
1003 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1006 /* This feature does not work; animation needs a rewrite */
1007 appData.highlightDragging = FALSE;
1011 gameInfo.variant = StringToVariant(appData.variant);
1012 InitPosition(FALSE);
1015 * determine size, based on supplied or remembered -size, or screen size
1017 if (isdigit(appData.boardSize[0])) {
1018 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1019 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1020 &fontPxlSize, &smallLayout, &tinyLayout);
1022 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1023 programName, appData.boardSize);
1027 /* Find some defaults; use the nearest known size */
1028 SizeDefaults *szd, *nearest;
1029 int distance = 99999;
1030 nearest = szd = sizeDefaults;
1031 while (szd->name != NULL) {
1032 if (abs(szd->squareSize - squareSize) < distance) {
1034 distance = abs(szd->squareSize - squareSize);
1035 if (distance == 0) break;
1039 if (i < 2) lineGap = nearest->lineGap;
1040 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1041 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1042 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1043 if (i < 6) smallLayout = nearest->smallLayout;
1044 if (i < 7) tinyLayout = nearest->tinyLayout;
1047 SizeDefaults *szd = sizeDefaults;
1048 if (*appData.boardSize == NULLCHAR) {
1049 // GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow)); // TODO: this does not work, as no mainwindow yet
1050 GdkScreen *screen = gdk_screen_get_default();
1051 guint screenwidth = gdk_screen_get_width(screen);
1052 guint screenheight = gdk_screen_get_height(screen);
1053 while (screenwidth < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1054 screenheight < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1057 if (szd->name == NULL) szd--;
1058 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1060 while (szd->name != NULL &&
1061 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1062 if (szd->name == NULL) {
1063 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1064 programName, appData.boardSize);
1068 squareSize = szd->squareSize;
1069 lineGap = szd->lineGap;
1070 clockFontPxlSize = szd->clockFontPxlSize;
1071 coordFontPxlSize = szd->coordFontPxlSize;
1072 fontPxlSize = szd->fontPxlSize;
1073 smallLayout = szd->smallLayout;
1074 tinyLayout = szd->tinyLayout;
1075 // [HGM] font: use defaults from settings file if available and not overruled
1077 initialSquareSize = squareSize; // [HGM] remember for saving font info
1078 if(BOARD_WIDTH != 8) {
1079 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // keep width the same
1080 lineGap = (squareSize < 37 ? 1 : squareSize < 59 ? 2 : squareSize < 116 ? 3 : 4);
1083 defaultLineGap = lineGap;
1084 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1086 /* [HR] height treated separately (hacked) */
1087 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1088 // boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1091 * Determine what fonts to use.
1093 InitializeFonts((2*clockFontPxlSize+1)/3, coordFontPxlSize, fontPxlSize);
1096 * Detect if there are not enough colors available and adapt.
1099 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1100 appData.monoMode = True;
1104 forceMono = MakeColors();
1107 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1109 appData.monoMode = True;
1112 ParseIcsTextColors();
1118 layoutName = "tinyLayout";
1119 } else if (smallLayout) {
1120 layoutName = "smallLayout";
1122 layoutName = "normalLayout";
1125 if(appData.logoSize) appData.logoSize = boardWidth/4-3;
1126 wpMain.width = -1; // prevent popup sizes window
1127 optList = BoardPopUp(squareSize, lineGap, (void*)
1137 InitDrawingHandle(optList + W_BOARD);
1138 shellWidget = shells[BoardWindow];
1139 currBoard = &optList[W_BOARD];
1140 boardWidget = optList[W_BOARD].handle;
1141 menuBarWidget = optList[W_MENU].handle;
1142 dropMenu = optList[W_DROP].handle;
1143 titleWidget = optList[optList[W_TITLE].type != Skip ? W_TITLE : W_SMALL].handle;
1145 formWidget = XtParent(boardWidget);
1146 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1147 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1148 XtGetValues(optList[W_WHITE].handle, args, 2);
1149 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1150 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1151 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1152 XtGetValues(optList[W_PAUSE].handle, args, 2);
1156 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1157 // not need to go into InitDrawingSizes().
1161 // add accelerators to main shell
1162 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1165 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1167 WhiteIcon = LoadIconFile("icon_white");
1168 BlackIcon = LoadIconFile("icon_black");
1169 SetClockIcon(0); // sets white icon
1173 * Create a cursor for the board widget.
1176 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1177 XChangeWindowAttributes(xDisplay, xBoardWindow,
1178 CWCursor, &window_attributes);
1182 * Inhibit shell resizing.
1185 shellArgs[0].value = (XtArgVal) &w;
1186 shellArgs[1].value = (XtArgVal) &h;
1187 XtGetValues(shellWidget, shellArgs, 2);
1188 shellArgs[4].value = shellArgs[2].value = w;
1189 shellArgs[5].value = shellArgs[3].value = h;
1190 // XtSetValues(shellWidget, &shellArgs[2], 4);
1193 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1194 // It wil only become known asynchronously, when we first write a string into it.
1195 // This will then change the clock widget height, which triggers resizing the top-level window
1196 // and a configure event. Only then can we know the total height of the top-level window,
1197 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1198 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1201 gtk_widget_get_allocation(shells[BoardWindow], &a);
1202 w = a.width; h = a.height;
1203 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1204 clockKludge = hc = a.height;
1205 gtk_widget_get_allocation(boardWidget, &a);
1206 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1207 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1213 if(appData.logoSize)
1214 { // locate and read user logo
1215 char buf[MSG_SIZ], name[MSG_SIZ];
1216 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1217 if(!FindLogo(name, ".logo", buf))
1218 FindLogo(appData.logoDir, name + 6, buf);
1219 ASSIGN(userLogo, buf);
1222 if (appData.animate || appData.animateDragging)
1225 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1226 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1228 /* [AS] Restore layout */
1229 if( wpMoveHistory.visible ) {
1233 if( wpEvalGraph.visible )
1238 if( wpEngineOutput.visible ) {
1239 EngineOutputPopUp();
1242 if( wpConsole.visible && appData.icsActive ) {
1247 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1252 if (errorExitStatus == -1) {
1253 if (appData.icsActive) {
1254 /* We now wait until we see "login:" from the ICS before
1255 sending the logon script (problems with timestamp otherwise) */
1256 /*ICSInitScript();*/
1257 if (appData.icsInputBox) ICSInputBoxPopUp();
1261 signal(SIGWINCH, TermSizeSigHandler);
1263 signal(SIGINT, IntSigHandler);
1264 signal(SIGTERM, IntSigHandler);
1265 if (*appData.cmailGameName != NULLCHAR) {
1266 signal(SIGUSR1, CmailSigHandler);
1271 // XtSetKeyboardFocus(shellWidget, formWidget);
1273 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1276 /* check for GTK events and process them */
1279 gtk_main_iteration();
1282 if (appData.debugMode) fclose(debugFP); // [DM] debug
1289 while(gtk_events_pending()) gtk_main_iteration();
1293 TermSizeSigHandler (int sig)
1299 IntSigHandler (int sig)
1305 CmailSigHandler (int sig)
1310 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1312 /* Activate call-back function CmailSigHandlerCallBack() */
1313 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1315 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1319 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1322 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1324 /**** end signal code ****/
1327 #define Abs(n) ((n)<0 ? -(n) : (n))
1330 InsertPxlSize (char *pattern, int targetPxlSize)
1333 snprintf(buf, MSG_SIZ, pattern, targetPxlSize); // pattern is something like "Sans Bold %d"
1340 InsertPxlSize (char *pattern, int targetPxlSize)
1342 char *base_fnt_lst, strInt[12], *p, *q;
1343 int alternatives, i, len, strIntLen;
1346 * Replace the "*" (if present) in the pixel-size slot of each
1347 * alternative with the targetPxlSize.
1351 while ((p = strchr(p, ',')) != NULL) {
1355 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1356 strIntLen = strlen(strInt);
1357 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1361 while (alternatives--) {
1362 char *comma = strchr(p, ',');
1363 for (i=0; i<14; i++) {
1364 char *hyphen = strchr(p, '-');
1366 if (comma && hyphen > comma) break;
1367 len = hyphen + 1 - p;
1368 if (i == 7 && *p == '*' && len == 2) {
1370 memcpy(q, strInt, strIntLen);
1380 len = comma + 1 - p;
1387 return base_fnt_lst;
1393 CreateFontSet (char *base_fnt_lst)
1396 char **missing_list;
1400 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1401 &missing_list, &missing_count, &def_string);
1402 if (appData.debugMode) {
1404 XFontStruct **font_struct_list;
1405 char **font_name_list;
1406 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1408 fprintf(debugFP, " got list %s, locale %s\n",
1409 XBaseFontNameListOfFontSet(fntSet),
1410 XLocaleOfFontSet(fntSet));
1411 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1412 for (i = 0; i < count; i++) {
1413 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1416 for (i = 0; i < missing_count; i++) {
1417 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1420 if (fntSet == NULL) {
1421 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1427 #else // not ENABLE_NLS
1429 * Find a font that matches "pattern" that is as close as
1430 * possible to the targetPxlSize. Prefer fonts that are k
1431 * pixels smaller to fonts that are k pixels larger. The
1432 * pattern must be in the X Consortium standard format,
1433 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1434 * The return value should be freed with XtFree when no
1439 FindFont (char *pattern, int targetPxlSize)
1441 char **fonts, *p, *best, *scalable, *scalableTail;
1442 int i, j, nfonts, minerr, err, pxlSize;
1444 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1446 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1447 programName, pattern);
1454 for (i=0; i<nfonts; i++) {
1457 if (*p != '-') continue;
1459 if (*p == NULLCHAR) break;
1460 if (*p++ == '-') j++;
1462 if (j < 7) continue;
1465 scalable = fonts[i];
1468 err = pxlSize - targetPxlSize;
1469 if (Abs(err) < Abs(minerr) ||
1470 (minerr > 0 && err < 0 && -err == minerr)) {
1476 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1477 /* If the error is too big and there is a scalable font,
1478 use the scalable font. */
1479 int headlen = scalableTail - scalable;
1480 p = (char *) XtMalloc(strlen(scalable) + 10);
1481 while (isdigit(*scalableTail)) scalableTail++;
1482 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1484 p = (char *) XtMalloc(strlen(best) + 2);
1485 safeStrCpy(p, best, strlen(best)+1 );
1487 if (appData.debugMode) {
1488 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1489 pattern, targetPxlSize, p);
1491 XFreeFontNames(fonts);
1498 MarkMenuItem (char *menuRef, int state)
1500 MenuItem *item = MenuNameToItem(menuRef);
1502 if(item && item->handle) {
1503 ((GtkCheckMenuItem *) (item->handle))->active = state;
1509 EnableNamedMenuItem (char *menuRef, int state)
1511 MenuItem *item = MenuNameToItem(menuRef);
1513 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1518 EnableButtonBar (int state)
1521 XtSetSensitive(optList[W_BUTTON].handle, state);
1527 SetMenuEnables (Enables *enab)
1529 while (enab->name != NULL) {
1530 EnableNamedMenuItem(enab->name, enab->value);
1535 gboolean KeyPressProc(window, eventkey, data)
1537 GdkEventKey *eventkey;
1541 MoveTypeInProc(eventkey); // pop up for typed in moves
1544 /* check for other key values */
1545 switch(eventkey->keyval) {
1557 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1558 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1560 if(*nprms == 0) return;
1561 item = MenuNameToItem(prms[0]);
1562 if(item) ((MenuProc *) item->proc) ();
1576 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1577 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1578 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1579 dmEnables[i].piece);
1580 XtSetSensitive(entry, p != NULL || !appData.testLegality
1581 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1582 && !appData.icsActive));
1584 while (p && *p++ == dmEnables[i].piece) count++;
1585 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1587 XtSetArg(args[j], XtNlabel, label); j++;
1588 XtSetValues(entry, args, j);
1594 do_flash_delay (unsigned long msec)
1600 FlashDelay (int flash_delay)
1602 if(flash_delay) do_flash_delay(flash_delay);
1606 Fraction (int x, int start, int stop)
1608 double f = ((double) x - start)/(stop - start);
1609 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1613 static WindowPlacement wpNew;
1616 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1618 int touch=0, fudge = 4, f = 3;
1619 GetActualPlacement(sh, wp);
1620 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1621 if(abs(wp->x + wp->width + 2*frameX - f - wpMain.x) < fudge) touch = 2; else // left touch
1622 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1623 if(abs(wp->y + wp->height + frameX + frameY - f - wpMain.y) < fudge) touch = 4; // top touch
1624 //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);
1625 if(!touch ) return; // only windows that touch co-move
1626 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1627 int heightInc = wpNew.height - wpMain.height;
1628 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1629 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1630 wp->y += fracTop * heightInc;
1631 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1633 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1635 wp->height += heightInc;
1636 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1637 int widthInc = wpNew.width - wpMain.width;
1638 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1639 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1640 wp->y += fracLeft * widthInc;
1641 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1643 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1645 wp->width += widthInc;
1647 wp->x += wpNew.x - wpMain.x;
1648 wp->y += wpNew.y - wpMain.y;
1649 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1650 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1652 XtSetArg(args[j], XtNx, wp->x); j++;
1653 XtSetArg(args[j], XtNy, wp->y); j++;
1654 XtSetValues(sh, args, j);
1656 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1657 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1658 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1662 ReSize (WindowPlacement *wp)
1665 int sqx, sqy, i, w, h, lg = lineGap;
1666 static int first = 1;
1667 // DisplayBothClocks();
1668 if(wp->width == wpMain.width && wp->height == wpMain.height && !first) return; // not sized
1669 gtk_widget_get_allocation(optList[W_DROP+1].handle, &a); // table that should contain everything
1670 w = a.width; h = a.height;
1671 gtk_widget_get_allocation(shellWidget, &a);
1672 if(a.width < w || a.height < h) { // outer window smaller than dialog content?
1673 w = a.width - w; h = a.height - h; // subtract matrgins, measured as table minus board dimensions
1674 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1675 w += a.width; h += a.height;
1677 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1678 w = a.width; h = a.height;
1680 sqx = (w - lg) / BOARD_WIDTH - lg;
1681 sqy = (h - lg) / BOARD_HEIGHT - lg;
1682 if(sqy < sqx) sqx = sqy;
1683 if(sqx < 20) return;
1684 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1686 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1687 sqx = (w - lg) / BOARD_WIDTH - lg;
1688 sqy = (h - lg) / BOARD_HEIGHT - lg;
1689 if(sqy < sqx) sqx = sqy;
1690 lg = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1691 if(sqx == oldSqx + 1 && lg == lineGap + 1) sqx = oldSqx, squareSize = 0; // prevent oscillations, force resize by kludge
1693 for(h=0; sizeDefaults[h].name && sizeDefaults[h].squareSize*8 > sqx*BOARD_WIDTH; h++) {}
1694 if(initialSquareSize != sizeDefaults[h].squareSize) { // boardSize changed
1695 initialSquareSize = sizeDefaults[h].squareSize; // used for saving font
1696 if(!fontValid[CLOCK_FONT][initialSquareSize]) fontTable[CLOCK_FONT][initialSquareSize] = CLOCK_FONT_NAME;
1697 appData.clockFont = InsertPxlSize(fontTable[CLOCK_FONT][initialSquareSize], 2*(sizeDefaults[h].clockFontPxlSize+1)/3);
1698 if(!fontValid[MESSAGE_FONT][initialSquareSize]) fontTable[MESSAGE_FONT][initialSquareSize] = DEFAULT_FONT_NAME;
1699 appData.font = InsertPxlSize(fontTable[MESSAGE_FONT][initialSquareSize], sizeDefaults[h].coordFontPxlSize);
1700 DisplayBothClocks();
1701 ApplyFont(&mainOptions[W_MESSG], NULL);
1702 for(i=1; i<6; i++) ApplyFont(&mainOptions[W_BUTTON+i], NULL);
1704 if(!strchr(appData.boardSize, ',')) {
1705 ASSIGN(appData.boardSize, sizeDefaults[h].name);
1708 if(sizeDefaults[h].tinyLayout != tinyLayout) { // alter clipping of menu names to conform to board width
1709 int clip = (tinyLayout = sizeDefaults[h].tinyLayout) + 1;
1711 for(h=1; mainOptions[h].type == DropDown; h++) {
1712 strncpy(text, _(mainOptions[h].name), MSG_SIZ);
1713 if(clip != 1) text[clip + (text[clip-1] == '_')] = NULLCHAR;
1714 gtk_menu_item_set_label((GtkMenuItem *) mainOptions[h].handle, text);
1718 if(sqx != squareSize && !first) {
1719 squareSize = sqx; // adopt new square size
1720 CreatePNGPieces(); // make newly scaled pieces
1721 InitDrawingSizes(0, 0); // creates grid etc.
1722 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1723 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1724 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1725 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1726 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1727 first = appData.fixedSize;
1728 if(twoBoards && shellUp[DummyDlg]) {
1729 SlavePopUp(); dualOptions[3].max = 0; DoEvents(); // calls SlaveResize, kludge to force assigning new canvas
1730 partnerUp = !partnerUp; flipView = !flipView;
1731 DrawPosition(True, NULL);
1732 partnerUp = !partnerUp; flipView = !flipView;
1736 static guint delayedDragTag = 0;
1742 if(busy) { // prevent recursive calling, but postpone interrupting call rather than lose it
1743 if(!delayedDragTag) delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1747 GetActualPlacement(shellWidget, &wpNew);
1748 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1749 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1750 busy = 0; return; // false alarm
1753 if(appData.useStickyWindows) {
1754 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1755 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1756 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1757 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1758 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1761 DrawPosition(True, NULL);
1762 if(delayedDragTag) g_source_remove(delayedDragTag);
1763 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1770 //printf("old timr = %d\n", delayedDragTag);
1771 if(delayedDragTag) g_source_remove(delayedDragTag);
1772 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1773 //printf("new timr = %d\n", delayedDragTag);
1777 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1779 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1781 wpNew.x = event->configure.x;
1782 wpNew.y = event->configure.y;
1783 wpNew.width = event->configure.width;
1784 wpNew.height = event->configure.height;
1785 // SetWidgetLabel(&mainOptions[W_WHITE], ""); SetWidgetLabel(&mainOptions[W_BLACK], "");
1786 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1792 /* Disable all user input other than deleting the window */
1793 static int frozen = 0;
1799 /* Grab by a widget that doesn't accept input */
1800 gtk_grab_add(optList[W_MESSG].handle);
1804 /* Undo a FreezeUI */
1808 if (!frozen) return;
1809 gtk_grab_remove(optList[W_MESSG].handle);
1816 static int oldPausing = FALSE;
1817 static GameMode oldMode = (GameMode) -1;
1819 if (!boardWidget) return;
1821 if (pausing != oldPausing) {
1822 oldPausing = pausing;
1823 MarkMenuItem("Mode.Pause", pausing);
1825 if (appData.showButtonBar) {
1826 /* Always toggle, don't set. Previous code messes up when
1827 invoked while the button is pressed, as releasing it
1828 toggles the state again. */
1830 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1831 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1835 wname = ModeToWidgetName(oldMode);
1836 if (wname != NULL) {
1837 MarkMenuItem(wname, False);
1839 wname = ModeToWidgetName(gameMode);
1840 if (wname != NULL) {
1841 MarkMenuItem(wname, True);
1843 if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
1844 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1847 /* Maybe all the enables should be handled here, not just this one */
1848 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1850 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1855 * Button/menu procedures
1858 void CopyFileToClipboard(gchar *filename)
1860 gchar *selection_tmp;
1864 FILE* f = fopen(filename, "r");
1867 if (f == NULL) return;
1871 selection_tmp = g_try_malloc(len + 1);
1872 if (selection_tmp == NULL) {
1873 printf("Malloc failed in CopyFileToClipboard\n");
1876 count = fread(selection_tmp, 1, len, f);
1879 g_free(selection_tmp);
1882 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1884 // copy selection_tmp to clipboard
1885 GdkDisplay *gdisp = gdk_display_get_default();
1887 g_free(selection_tmp);
1890 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1891 gtk_clipboard_set_text(cb, selection_tmp, -1);
1892 g_free(selection_tmp);
1896 CopySomething (char *src)
1898 GdkDisplay *gdisp = gdk_display_get_default();
1900 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1901 if (gdisp == NULL) return;
1902 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1903 gtk_clipboard_set_text(cb, src, -1);
1907 PastePositionProc ()
1909 GdkDisplay *gdisp = gdk_display_get_default();
1913 if (gdisp == NULL) return;
1914 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1915 fenstr = gtk_clipboard_wait_for_text(cb);
1916 if (fenstr==NULL) return; // nothing had been selected to copy
1917 EditPositionPasteFEN(fenstr);
1926 guint len=0; int flip = appData.flipView;
1929 // get game from clipboard
1930 GdkDisplay *gdisp = gdk_display_get_default();
1931 if (gdisp == NULL) return;
1932 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1933 text = gtk_clipboard_wait_for_text(cb);
1934 if (text == NULL) return; // nothing to paste
1937 // write to temp file
1938 if (text == NULL || len == 0) {
1939 return; //nothing to paste
1941 f = fopen(gamePasteFilename, "w");
1943 DisplayError(_("Can't open temp file"), errno);
1946 fwrite(text, 1, len, f);
1950 if(!appData.autoFlipView) appData.flipView = flipView;
1951 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1952 appData.flipView = flip;
1959 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1965 void MoveTypeInProc(eventkey)
1966 GdkEventKey *eventkey;
1970 // ingnore if ctrl, alt, or meta is pressed
1971 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1975 buf[0]=eventkey->keyval;
1977 if (eventkey->keyval > 32 && eventkey->keyval < 256 || *buf == 27)
1978 ConsoleAutoPopUp (buf);
1983 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1985 if (!TempBackwardActive) {
1986 TempBackwardActive = True;
1992 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1994 /* Check to see if triggered by a key release event for a repeating key.
1995 * If so the next queued event will be a key press of the same key at the same time */
1996 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1998 XPeekEvent(xDisplay, &next);
1999 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2000 next.xkey.keycode == event->xkey.keycode)
2004 TempBackwardActive = False;
2010 { // called from menu
2013 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"man %s/../man/man6/xboard.6\"' -e 'end tell'", dataDir);
2016 system("xterm -e man xboard &");
2025 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);
2027 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
2035 SetWindowTitle (char *text, char *title, char *icon)
2040 if (appData.titleInWindow) {
2042 XtSetArg(args[i], XtNlabel, text); i++;
2043 XtSetValues(titleWidget, args, i);
2046 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2047 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2048 XtSetValues(shellWidget, args, i);
2049 XSync(xDisplay, False);
2051 if (appData.titleInWindow) {
2052 SetWidgetLabel(titleWidget, text);
2054 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2059 DisplayIcsInteractionTitle (String message)
2062 if (oldICSInteractionTitle == NULL) {
2063 /* Magic to find the old window title, adapted from vim */
2064 char *wina = getenv("WINDOWID");
2066 Window win = (Window) atoi(wina);
2067 Window root, parent, *children;
2068 unsigned int nchildren;
2069 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2071 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2072 if (!XQueryTree(xDisplay, win, &root, &parent,
2073 &children, &nchildren)) break;
2074 if (children) XFree((void *)children);
2075 if (parent == root || parent == 0) break;
2078 XSetErrorHandler(oldHandler);
2080 if (oldICSInteractionTitle == NULL) {
2081 oldICSInteractionTitle = "xterm";
2084 printf("\033]0;%s\007", message);
2090 LockBoardSize (int after)
2092 static char *oldClockFont, *oldMessgFont;
2094 if(oldMessgFont && !strcmp(oldMessgFont, appData.font) &&
2095 oldClockFont && !strcmp(oldClockFont, appData.clockFont) ) return; // only do something when font changed
2096 w = BOARD_WIDTH*(squareSize + lineGap) + lineGap;
2097 h = BOARD_HEIGHT*(squareSize + lineGap) + lineGap;
2099 ASSIGN(oldClockFont, appData.clockFont);
2100 ASSIGN(oldMessgFont, appData.font);
2101 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
2103 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board
2105 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
2110 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2112 GtkWidget *w = (GtkWidget *) opt->handle;
2119 strcpy(bgcolor, "black");
2120 strcpy(fgcolor, "white");
2122 strcpy(bgcolor, "white");
2123 strcpy(fgcolor, "black");
2126 appData.lowTimeWarning &&
2127 (timer / 1000) < appData.icsAlarmTime) {
2128 strcpy(fgcolor, appData.lowTimeWarningColor);
2131 gdk_color_parse( bgcolor, &col );
2132 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2134 if (appData.clockMode) {
2135 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2136 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2137 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2138 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2140 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2141 bgcolor, fgcolor, color);
2142 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2143 // bgcolor, fgcolor, color);
2145 gtk_label_set_markup(GTK_LABEL(w), markup);
2149 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2152 SetClockIcon (int color)
2154 GdkPixbuf *pm = *clockIcons[color];
2155 if (mainwindowIcon != pm) {
2156 mainwindowIcon = pm;
2158 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2160 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2165 #define INPUT_SOURCE_BUF_SIZE 8192
2174 char buf[INPUT_SOURCE_BUF_SIZE];
2179 DoInputCallback(io, cond, data)
2184 /* read input from one of the input source (for example a chess program, ICS, etc).
2185 * and call a function that will handle the input
2192 /* All information (callback function, file descriptor, etc) is
2193 * saved in an InputSource structure
2195 InputSource *is = (InputSource *) data;
2197 if (is->lineByLine) {
2198 count = read(is->fd, is->unused,
2199 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2201 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2202 RemoveInputSource(is); // cease reading stdin
2203 stdoutClosed = TRUE; // suppress future output
2206 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2209 is->unused += count;
2211 /* break input into lines and call the callback function on each
2214 while (p < is->unused) {
2215 q = memchr(p, '\n', is->unused - p);
2216 if (q == NULL) break;
2218 (is->func)(is, is->closure, p, q - p, 0);
2221 /* remember not yet used part of the buffer */
2223 while (p < is->unused) {
2228 /* read maximum length of input buffer and send the whole buffer
2229 * to the callback function
2231 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2236 (is->func)(is, is->closure, is->buf, count, error);
2238 return True; // Must return true or the watch will be removed
2241 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2248 GIOChannel *channel;
2249 ChildProc *cp = (ChildProc *) pr;
2251 is = (InputSource *) calloc(1, sizeof(InputSource));
2252 is->lineByLine = lineByLine;
2256 is->fd = fileno(stdin);
2258 is->kind = cp->kind;
2259 is->fd = cp->fdFrom;
2262 is->unused = is->buf;
2266 /* GTK-TODO: will this work on windows?*/
2268 channel = g_io_channel_unix_new(is->fd);
2269 g_io_channel_set_close_on_unref (channel, TRUE);
2270 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2272 is->closure = closure;
2273 return (InputSourceRef) is;
2278 RemoveInputSource(isr)
2281 InputSource *is = (InputSource *) isr;
2283 if (is->sid == 0) return;
2284 g_source_remove(is->sid);
2291 static Boolean frameWaiting;
2294 FrameAlarm (int sig)
2296 frameWaiting = False;
2297 /* In case System-V style signals. Needed?? */
2298 signal(SIGALRM, FrameAlarm);
2302 FrameDelay (int time)
2304 struct itimerval delay;
2307 frameWaiting = True;
2308 signal(SIGALRM, FrameAlarm);
2309 delay.it_interval.tv_sec =
2310 delay.it_value.tv_sec = time / 1000;
2311 delay.it_interval.tv_usec =
2312 delay.it_value.tv_usec = (time % 1000) * 1000;
2313 setitimer(ITIMER_REAL, &delay, NULL);
2314 while (frameWaiting) pause();
2315 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2316 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2317 setitimer(ITIMER_REAL, &delay, NULL);
2324 FrameDelay (int time)
2327 XSync(xDisplay, False);
2329 // gtk_main_iteration_do(False);
2332 usleep(time * 1000);
2338 FindLogo (char *place, char *name, char *buf)
2339 { // check if file exists in given place
2341 if(!place) return 0;
2342 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2343 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2351 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2353 char buf[MSG_SIZ], *logoName = buf;
2354 if(appData.logo[n][0]) {
2355 logoName = appData.logo[n];
2356 } else if(appData.autoLogo) {
2357 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2358 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2359 } else { // engine; cascade
2360 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2361 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2362 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2363 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2367 { ASSIGN(cps->programLogo, logoName); }
2371 UpdateLogos (int displ)
2373 if(optList[W_WHITE-1].handle == NULL) return;
2374 LoadLogo(&first, 0, 0);
2375 LoadLogo(&second, 1, appData.icsActive);
2376 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2380 void FileNamePopUpWrapper(
2391 GtkFileFilter *gtkfilter;
2392 GtkFileFilter *gtkfilter_all;
2394 char fileext[10] = "";
2395 char *result = NULL;
2397 char curDir[MSG_SIZ];
2399 StartDir(filter, NULL); // change to start directory for this file type
2401 if(def && *def && def[strlen(def)-1] == '/') {
2402 getcwd(curDir, MSG_SIZ);
2407 /* make a copy of the filter string, so that strtok can work with it*/
2408 cp = strdup(filter);
2410 /* add filters for file extensions */
2411 gtkfilter = gtk_file_filter_new();
2412 gtkfilter_all = gtk_file_filter_new();
2414 /* one filter to show everything */
2415 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2416 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2418 /* add filter if present */
2419 result = strtok(cp, space);
2420 while( result != NULL ) {
2421 snprintf(fileext,10,"*%s",result);
2422 result = strtok( NULL, space );
2423 gtk_file_filter_add_pattern(gtkfilter, fileext);
2426 /* second filter to only show what's useful */
2427 gtk_file_filter_set_name (gtkfilter,filter);
2429 if (openMode[0] == 'r')
2431 dialog = gtk_file_chooser_dialog_new (label,
2433 GTK_FILE_CHOOSER_ACTION_OPEN,
2434 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2435 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2440 dialog = gtk_file_chooser_dialog_new (label,
2442 GTK_FILE_CHOOSER_ACTION_SAVE,
2443 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2444 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2446 /* add filename suggestions */
2447 if (strlen(def) > 0 )
2448 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2450 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2454 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2455 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2456 /* activate filter */
2457 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2459 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2464 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2467 f = fopen(filename, openMode);
2470 DisplayError(_("Failed to open file"), errno);
2474 /* TODO add indec */
2476 ASSIGN(*name, filename);
2477 ScheduleDelayedEvent(DelayedLoad, 50);
2479 StartDir(filter, filename);
2482 else StartDir(filter, "");
2484 gtk_widget_destroy (dialog);
2487 if(def && *def && def[strlen(def)-1] == '/') chdir(curDir);