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 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
64 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.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).
175 // redefine some defaults
178 # undef SETTINGS_FILE
179 # define ICS_LOGON "Library/Preferences/XboardICS.conf"
180 # define DATADIR dataDir
181 # define SETTINGS_FILE masterSettings
182 char dataDir[MSG_SIZ]; // for expanding ~~
183 char masterSettings[MSG_SIZ];
192 #define usleep(t) _sleep2(((t)+500)/1000)
196 # define _(s) gettext (s)
197 # define N_(s) gettext_noop (s)
203 int main P((int argc, char **argv));
204 RETSIGTYPE CmailSigHandler P((int sig));
205 RETSIGTYPE IntSigHandler P((int sig));
206 RETSIGTYPE TermSizeSigHandler P((int sig));
208 char *InsertPxlSize P((char *pattern, int targetPxlSize));
209 XFontSet CreateFontSet P((char *base_fnt_lst));
211 char *FindFont P((char *pattern, int targetPxlSize));
213 void DelayedDrag P((void));
214 void ICSInputBoxPopUp P((void));
215 void MoveTypeInProc P((GdkEventKey *eventkey));
216 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
217 Boolean TempBackwardActive = False;
218 void DisplayMove P((int moveNumber));
219 void update_ics_width P(());
220 int CopyMemoProc P(());
221 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
225 XFontSet fontSet, clockFontSet;
228 XFontStruct *clockFontStruct;
230 Font coordFontID, countFontID;
231 XFontStruct *coordFontStruct, *countFontStruct;
233 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
234 GtkWidget *mainwindow;
236 Option *optList; // contains all widgets of main window
239 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
242 static GdkPixbuf *mainwindowIcon=NULL;
243 static GdkPixbuf *WhiteIcon=NULL;
244 static GdkPixbuf *BlackIcon=NULL;
246 /* key board accelerators */
247 GtkAccelGroup *GtkAccelerators;
249 typedef unsigned int BoardSize;
251 Boolean chessProgram;
253 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
254 int smallLayout = 0, tinyLayout = 0,
255 marginW, marginH, // [HGM] for run-time resizing
256 fromX = -1, fromY = -1, toX, toY, commentUp = False,
257 errorExitStatus = -1, defaultLineGap;
259 Dimension textHeight;
261 char *chessDir, *programName, *programVersion;
262 Boolean alwaysOnTop = False;
263 char *icsTextMenuString;
265 char *firstChessProgramNames;
266 char *secondChessProgramNames;
268 WindowPlacement wpMain;
269 WindowPlacement wpConsole;
270 WindowPlacement wpComment;
271 WindowPlacement wpMoveHistory;
272 WindowPlacement wpEvalGraph;
273 WindowPlacement wpEngineOutput;
274 WindowPlacement wpGameList;
275 WindowPlacement wpTags;
276 WindowPlacement wpDualBoard;
278 /* This magic number is the number of intermediate frames used
279 in each half of the animation. For short moves it's reduced
280 by 1. The total number of frames will be factor * 2 + 1. */
283 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
290 DropMenuEnables dmEnables[] = {
299 XtResource clientResources[] = {
300 { "flashCount", "flashCount", XtRInt, sizeof(int),
301 XtOffset(AppDataPtr, flashCount), XtRImmediate,
302 (XtPointer) FLASH_COUNT },
306 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
307 char globalTranslations[] =
308 ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
309 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
310 :<KeyDown>Return: TempBackwardProc() \n \
311 :<KeyUp>Return: TempForwardProc() \n";
313 char ICSInputTranslations[] =
314 "<Key>Up: UpKeyProc() \n "
315 "<Key>Down: DownKeyProc() \n "
316 "<Key>Return: EnterKeyProc() \n";
318 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
319 // as the widget is destroyed before the up-click can call extend-end
320 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
323 String xboardResources[] = {
324 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
332 gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
335 //---------------------------------------------------------------------------------------------------------
336 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
339 #define CW_USEDEFAULT (1<<31)
340 #define ICS_TEXT_MENU_SIZE 90
341 #define DEBUG_FILE "xboard.debug"
342 #define SetCurrentDirectory chdir
343 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
347 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
350 // front-end part of option handling
352 // [HGM] This platform-dependent table provides the location for storing the color info
353 extern char *crWhite, * crBlack;
357 &appData.whitePieceColor,
358 &appData.blackPieceColor,
359 &appData.lightSquareColor,
360 &appData.darkSquareColor,
361 &appData.highlightSquareColor,
362 &appData.premoveHighlightColor,
363 &appData.lowTimeWarningColor,
374 // [HGM] font: keep a font for each square size, even non-stndard ones
377 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
378 char *fontTable[NUM_FONTS][MAX_SIZE];
381 ParseFont (char *name, int number)
382 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
384 if(sscanf(name, "size%d:", &size)) {
385 // [HGM] font: font is meant for specific boardSize (likely from settings file);
386 // defer processing it until we know if it matches our board size
387 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
388 fontTable[number][size] = strdup(strchr(name, ':')+1);
389 fontValid[number][size] = True;
394 case 0: // CLOCK_FONT
395 appData.clockFont = strdup(name);
397 case 1: // MESSAGE_FONT
398 appData.font = strdup(name);
400 case 2: // COORD_FONT
401 appData.coordFont = strdup(name);
406 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
411 { // only 2 fonts currently
412 appData.clockFont = CLOCK_FONT_NAME;
413 appData.coordFont = COORD_FONT_NAME;
414 appData.font = DEFAULT_FONT_NAME;
419 { // no-op, until we identify the code for this already in XBoard and move it here
423 ParseColor (int n, char *name)
424 { // in XBoard, just copy the color-name string
425 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
429 ParseTextAttribs (ColorClass cc, char *s)
431 (&appData.colorShout)[cc] = strdup(s);
435 ParseBoardSize (void *addr, char *name)
437 appData.boardSize = strdup(name);
442 { // In XBoard the sound-playing program takes care of obtaining the actual sound
446 SetCommPortDefaults ()
447 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
450 // [HGM] args: these three cases taken out to stay in front-end
452 SaveFontArg (FILE *f, ArgDescriptor *ad)
455 int i, n = (int)(intptr_t)ad->argLoc;
457 case 0: // CLOCK_FONT
458 name = appData.clockFont;
460 case 1: // MESSAGE_FONT
463 case 2: // COORD_FONT
464 name = appData.coordFont;
469 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
470 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
471 fontTable[n][squareSize] = strdup(name);
472 fontValid[n][squareSize] = True;
475 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
476 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
481 { // nothing to do, as the sounds are at all times represented by their text-string names already
485 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
486 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
487 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
491 SaveColor (FILE *f, ArgDescriptor *ad)
492 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
493 if(colorVariable[(int)(intptr_t)ad->argLoc])
494 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
498 SaveBoardSize (FILE *f, char *name, void *addr)
499 { // wrapper to shield back-end from BoardSize & sizeInfo
500 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
504 ParseCommPortSettings (char *s)
505 { // no such option in XBoard (yet)
511 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
515 gtk_widget_get_allocation(shell, &a);
516 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
520 wp->height = a.height;
521 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
522 frameX = 3; frameY = 3; // remember to decide if windows touch
526 GetPlacement (DialogClass dlg, WindowPlacement *wp)
527 { // wrapper to shield back-end from widget type
528 if(shellUp[dlg]) GetActualPlacement(shells[dlg], wp);
533 { // wrapper to shield use of window handles from back-end (make addressible by number?)
534 // In XBoard this will have to wait until awareness of window parameters is implemented
535 GetActualPlacement(shellWidget, &wpMain);
536 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
537 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
538 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
539 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
540 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
541 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
542 GetPlacement(ChatDlg, &wpConsole); if(appData.icsActive) wpConsole.visible = shellUp[ChatDlg];
546 PrintCommPortSettings (FILE *f, char *name)
547 { // This option does not exist in XBoard
551 EnsureOnScreen (int *x, int *y, int minX, int minY)
558 { // [HGM] args: allows testing if main window is realized from back-end
559 return DialogExists(BoardWindow);
563 PopUpStartupDialog ()
564 { // start menu not implemented in XBoard
568 ConvertToLine (int argc, char **argv)
570 static char line[128*1024], buf[1024];
574 for(i=1; i<argc; i++)
576 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
577 && argv[i][0] != '{' )
578 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
580 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
581 strncat(line, buf, 128*1024 - strlen(line) - 1 );
584 line[strlen(line)-1] = NULLCHAR;
588 //--------------------------------------------------------------------------------------------
593 ResizeBoardWindow (int w, int h, int inhibit)
596 if(clockKludge) return; // ignore as long as clock does not have final height
597 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
598 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
599 h += marginH + a.height + 1;
600 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
605 { // dummy, as the GTK code does not make colors in advance
610 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
611 { // determine what fonts to use, and create them
616 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
617 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
618 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
619 appData.font = fontTable[MESSAGE_FONT][squareSize];
620 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
621 appData.coordFont = fontTable[COORD_FONT][squareSize];
624 appData.font = InsertPxlSize(appData.font, fontPxlSize);
625 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
626 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
627 fontSet = CreateFontSet(appData.font);
628 clockFontSet = CreateFontSet(appData.clockFont);
630 /* For the coordFont, use the 0th font of the fontset. */
631 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
632 XFontStruct **font_struct_list;
633 XFontSetExtents *fontSize;
634 char **font_name_list;
635 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
636 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
637 coordFontStruct = XQueryFont(xDisplay, coordFontID);
638 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
639 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
642 appData.font = FindFont(appData.font, fontPxlSize);
643 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
644 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
645 clockFontID = XLoadFont(xDisplay, appData.clockFont);
646 clockFontStruct = XQueryFont(xDisplay, clockFontID);
647 coordFontID = XLoadFont(xDisplay, appData.coordFont);
648 coordFontStruct = XQueryFont(xDisplay, coordFontID);
649 // textHeight in !NLS mode!
651 countFontID = coordFontID; // [HGM] holdings
652 countFontStruct = coordFontStruct;
654 xdb = XtDatabase(xDisplay);
656 XrmPutLineResource(&xdb, "*international: True");
657 vTo.size = sizeof(XFontSet);
658 vTo.addr = (XtPointer) &fontSet;
659 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
661 XrmPutStringResource(&xdb, "*font", appData.font);
672 case ArgInt: p = " N"; break;
673 case ArgString: p = " STR"; break;
674 case ArgBoolean: p = " TF"; break;
675 case ArgSettingsFilename:
676 case ArgBackupSettingsFile:
677 case ArgFilename: p = " FILE"; break;
678 case ArgX: p = " Nx"; break;
679 case ArgY: p = " Ny"; break;
680 case ArgAttribs: p = " TEXTCOL"; break;
681 case ArgColor: p = " COL"; break;
682 case ArgFont: p = " FONT"; break;
683 case ArgBoardSize: p = " SIZE"; break;
684 case ArgFloat: p = " FLOAT"; break;
689 case ArgCommSettings:
701 ArgDescriptor *q, *p = argDescriptors+5;
702 printf("\nXBoard accepts the following options:\n"
703 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
704 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
705 " SIZE = board-size spec(s)\n"
706 " Within parentheses are short forms, or options to set to true or false.\n"
707 " Persistent options (saved in the settings file) are marked with *)\n\n");
709 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
710 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
711 if(p->save) strcat(buf+len, "*");
712 for(q=p+1; q->argLoc == p->argLoc; q++) {
713 if(q->argName[0] == '-') continue;
714 strcat(buf+len, q == p+1 ? " (" : " ");
715 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
717 if(q != p+1) strcat(buf+len, ")");
719 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
722 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
726 SlaveResize (Option *opt)
728 static int slaveW, slaveH, w, h;
731 gtk_widget_get_allocation(shells[DummyDlg], &a);
732 w = a.width; h = a.height;
733 gtk_widget_get_allocation(opt->handle, &a);
734 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
735 slaveH = h - a.height + 13;
737 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
741 static char clickedFile[MSG_SIZ];
745 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
746 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
747 if(suppress) { // we just started XBoard without arguments
748 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
749 } else { // we are running something presumably useful
751 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
752 system(buf); // start new instance on this file
759 main (int argc, char **argv)
761 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
762 int boardWidth, w, h; //, boardHeight;
764 int forceMono = False;
766 srandom(time(0)); // [HGM] book: make random truly random
768 setbuf(stdout, NULL);
769 setbuf(stderr, NULL);
772 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
773 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
777 if(argc > 1 && !strcmp(argv[1], "--help" )) {
783 gtk_init (&argc, &argv);
785 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
786 GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
787 char *path = gtkosx_application_get_bundle_path();
788 strncpy(dataDir, path, MSG_SIZ);
789 snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
790 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
791 // we must call application ready before we can get the signal,
792 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
793 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
794 gtkosx_application_ready(theApp);
795 suppress = (argc == 1 || argc > 1 && argv[1][00] != '-'); // OSX sends signal even if name was already argv[1]!
796 if(argc == 1) { // called without args: OSX open-file signal might follow
797 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
798 usleep(10000); // wait 10 msec (and hope this is long enough).
799 while(gtk_events_pending())
800 gtk_main_iteration(); // process all events that came in upto now
801 suppress = 0; // future open-file signals should start new instance
802 if(clickedFile[0]) { // we were sent an open-file signal with filename!
803 fakeArgv[0] = argv[0];
804 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
810 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
811 typedef struct {char *name, *value; } Config;
812 static Config configList[] = {
813 { "Datadir", DATADIR },
814 { "Sysconfdir", SYSCONFDIR },
819 for(i=0; configList[i].name; i++) {
820 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
821 if(argc > 2) printf("%s", configList[i].value);
822 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
827 /* set up keyboard accelerators group */
828 GtkAccelerators = gtk_accel_group_new();
830 programName = strrchr(argv[0], '/');
831 if (programName == NULL)
832 programName = argv[0];
837 // if (appData.debugMode) {
838 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
841 bindtextdomain(PACKAGE, LOCALEDIR);
842 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
846 appData.boardSize = "";
847 InitAppData(ConvertToLine(argc, argv));
849 if (p == NULL) p = "/tmp";
850 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
851 gameCopyFilename = (char*) malloc(i);
852 gamePasteFilename = (char*) malloc(i);
853 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
854 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
856 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
857 static char buf[MSG_SIZ];
858 snprintf(buf, MSG_SIZ, appData.sysOpen, DATADIR);
859 ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
860 EscapeExpand(buf, appData.firstInitString);
861 appData.firstInitString = strdup(buf);
862 EscapeExpand(buf, appData.secondInitString);
863 appData.secondInitString = strdup(buf);
864 EscapeExpand(buf, appData.firstComputerString);
865 appData.firstComputerString = strdup(buf);
866 EscapeExpand(buf, appData.secondComputerString);
867 appData.secondComputerString = strdup(buf);
870 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
873 if (chdir(chessDir) != 0) {
874 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
880 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
881 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
882 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
883 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
886 setbuf(debugFP, NULL);
890 if (appData.debugMode) {
891 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
895 /* [HGM,HR] make sure board size is acceptable */
896 if(appData.NrFiles > BOARD_FILES ||
897 appData.NrRanks > BOARD_RANKS )
898 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
901 /* This feature does not work; animation needs a rewrite */
902 appData.highlightDragging = FALSE;
906 gameInfo.variant = StringToVariant(appData.variant);
910 * determine size, based on supplied or remembered -size, or screen size
912 if (isdigit(appData.boardSize[0])) {
913 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
914 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
915 &fontPxlSize, &smallLayout, &tinyLayout);
917 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
918 programName, appData.boardSize);
922 /* Find some defaults; use the nearest known size */
923 SizeDefaults *szd, *nearest;
924 int distance = 99999;
925 nearest = szd = sizeDefaults;
926 while (szd->name != NULL) {
927 if (abs(szd->squareSize - squareSize) < distance) {
929 distance = abs(szd->squareSize - squareSize);
930 if (distance == 0) break;
934 if (i < 2) lineGap = nearest->lineGap;
935 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
936 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
937 if (i < 5) fontPxlSize = nearest->fontPxlSize;
938 if (i < 6) smallLayout = nearest->smallLayout;
939 if (i < 7) tinyLayout = nearest->tinyLayout;
942 SizeDefaults *szd = sizeDefaults;
943 if (*appData.boardSize == NULLCHAR) {
944 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
945 guint screenwidth = gdk_screen_get_width(screen);
946 guint screenheight = gdk_screen_get_height(screen);
947 while (screenwidth < szd->minScreenSize ||
948 screenheight < szd->minScreenSize) {
951 if (szd->name == NULL) szd--;
952 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
954 while (szd->name != NULL &&
955 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
956 if (szd->name == NULL) {
957 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
958 programName, appData.boardSize);
962 squareSize = szd->squareSize;
963 lineGap = szd->lineGap;
964 clockFontPxlSize = szd->clockFontPxlSize;
965 coordFontPxlSize = szd->coordFontPxlSize;
966 fontPxlSize = szd->fontPxlSize;
967 smallLayout = szd->smallLayout;
968 tinyLayout = szd->tinyLayout;
969 // [HGM] font: use defaults from settings file if available and not overruled
972 defaultLineGap = lineGap;
973 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
975 /* [HR] height treated separately (hacked) */
976 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
977 // boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
980 * Determine what fonts to use.
983 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
987 * Detect if there are not enough colors available and adapt.
990 if (DefaultDepth(xDisplay, xScreen) <= 2) {
991 appData.monoMode = True;
995 forceMono = MakeColors();
998 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1000 appData.monoMode = True;
1003 ParseIcsTextColors();
1009 layoutName = "tinyLayout";
1010 } else if (smallLayout) {
1011 layoutName = "smallLayout";
1013 layoutName = "normalLayout";
1016 wpMain.width = -1; // prevent popup sizes window
1017 optList = BoardPopUp(squareSize, lineGap, (void*)
1027 InitDrawingHandle(optList + W_BOARD);
1028 shellWidget = shells[BoardWindow];
1029 currBoard = &optList[W_BOARD];
1030 boardWidget = optList[W_BOARD].handle;
1031 menuBarWidget = optList[W_MENU].handle;
1032 dropMenu = optList[W_DROP].handle;
1033 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1035 formWidget = XtParent(boardWidget);
1036 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1037 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1038 XtGetValues(optList[W_WHITE].handle, args, 2);
1039 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1040 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1041 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1042 XtGetValues(optList[W_PAUSE].handle, args, 2);
1046 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1047 // not need to go into InitDrawingSizes().
1051 // add accelerators to main shell
1052 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1055 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1057 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
1058 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
1059 mainwindowIcon = WhiteIcon;
1060 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1064 * Create a cursor for the board widget.
1067 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1068 XChangeWindowAttributes(xDisplay, xBoardWindow,
1069 CWCursor, &window_attributes);
1073 * Inhibit shell resizing.
1076 shellArgs[0].value = (XtArgVal) &w;
1077 shellArgs[1].value = (XtArgVal) &h;
1078 XtGetValues(shellWidget, shellArgs, 2);
1079 shellArgs[4].value = shellArgs[2].value = w;
1080 shellArgs[5].value = shellArgs[3].value = h;
1081 // XtSetValues(shellWidget, &shellArgs[2], 4);
1084 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1085 // It wil only become known asynchronously, when we first write a string into it.
1086 // This will then change the clock widget height, which triggers resizing the top-level window
1087 // and a configure event. Only then can we know the total height of the top-level window,
1088 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1089 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1092 gtk_widget_get_allocation(shells[BoardWindow], &a);
1093 w = a.width; h = a.height;
1094 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1095 clockKludge = hc = a.height;
1096 gtk_widget_get_allocation(boardWidget, &a);
1097 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1098 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1104 if(appData.logoSize)
1105 { // locate and read user logo
1107 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1108 ASSIGN(userLogo, buf);
1111 if (appData.animate || appData.animateDragging)
1114 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1115 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1117 /* [AS] Restore layout */
1118 if( wpMoveHistory.visible ) {
1122 if( wpEvalGraph.visible )
1127 if( wpEngineOutput.visible ) {
1128 EngineOutputPopUp();
1131 if( wpConsole.visible && appData.icsActive ) {
1138 if (errorExitStatus == -1) {
1139 if (appData.icsActive) {
1140 /* We now wait until we see "login:" from the ICS before
1141 sending the logon script (problems with timestamp otherwise) */
1142 /*ICSInitScript();*/
1143 if (appData.icsInputBox) ICSInputBoxPopUp();
1147 signal(SIGWINCH, TermSizeSigHandler);
1149 signal(SIGINT, IntSigHandler);
1150 signal(SIGTERM, IntSigHandler);
1151 if (*appData.cmailGameName != NULLCHAR) {
1152 signal(SIGUSR1, CmailSigHandler);
1156 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1159 // XtSetKeyboardFocus(shellWidget, formWidget);
1161 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1164 /* check for GTK events and process them */
1167 gtk_main_iteration();
1170 if (appData.debugMode) fclose(debugFP); // [DM] debug
1175 TermSizeSigHandler (int sig)
1181 IntSigHandler (int sig)
1187 CmailSigHandler (int sig)
1192 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1194 /* Activate call-back function CmailSigHandlerCallBack() */
1195 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1197 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1201 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1204 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1206 /**** end signal code ****/
1209 #define Abs(n) ((n)<0 ? -(n) : (n))
1213 InsertPxlSize (char *pattern, int targetPxlSize)
1215 char *base_fnt_lst, strInt[12], *p, *q;
1216 int alternatives, i, len, strIntLen;
1219 * Replace the "*" (if present) in the pixel-size slot of each
1220 * alternative with the targetPxlSize.
1224 while ((p = strchr(p, ',')) != NULL) {
1228 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1229 strIntLen = strlen(strInt);
1230 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1234 while (alternatives--) {
1235 char *comma = strchr(p, ',');
1236 for (i=0; i<14; i++) {
1237 char *hyphen = strchr(p, '-');
1239 if (comma && hyphen > comma) break;
1240 len = hyphen + 1 - p;
1241 if (i == 7 && *p == '*' && len == 2) {
1243 memcpy(q, strInt, strIntLen);
1253 len = comma + 1 - p;
1260 return base_fnt_lst;
1265 CreateFontSet (char *base_fnt_lst)
1268 char **missing_list;
1272 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1273 &missing_list, &missing_count, &def_string);
1274 if (appData.debugMode) {
1276 XFontStruct **font_struct_list;
1277 char **font_name_list;
1278 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1280 fprintf(debugFP, " got list %s, locale %s\n",
1281 XBaseFontNameListOfFontSet(fntSet),
1282 XLocaleOfFontSet(fntSet));
1283 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1284 for (i = 0; i < count; i++) {
1285 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1288 for (i = 0; i < missing_count; i++) {
1289 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1292 if (fntSet == NULL) {
1293 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1299 #else // not ENABLE_NLS
1301 * Find a font that matches "pattern" that is as close as
1302 * possible to the targetPxlSize. Prefer fonts that are k
1303 * pixels smaller to fonts that are k pixels larger. The
1304 * pattern must be in the X Consortium standard format,
1305 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1306 * The return value should be freed with XtFree when no
1310 FindFont (char *pattern, int targetPxlSize)
1312 char **fonts, *p, *best, *scalable, *scalableTail;
1313 int i, j, nfonts, minerr, err, pxlSize;
1316 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1318 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1319 programName, pattern);
1326 for (i=0; i<nfonts; i++) {
1329 if (*p != '-') continue;
1331 if (*p == NULLCHAR) break;
1332 if (*p++ == '-') j++;
1334 if (j < 7) continue;
1337 scalable = fonts[i];
1340 err = pxlSize - targetPxlSize;
1341 if (Abs(err) < Abs(minerr) ||
1342 (minerr > 0 && err < 0 && -err == minerr)) {
1348 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1349 /* If the error is too big and there is a scalable font,
1350 use the scalable font. */
1351 int headlen = scalableTail - scalable;
1352 p = (char *) XtMalloc(strlen(scalable) + 10);
1353 while (isdigit(*scalableTail)) scalableTail++;
1354 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1356 p = (char *) XtMalloc(strlen(best) + 2);
1357 safeStrCpy(p, best, strlen(best)+1 );
1359 if (appData.debugMode) {
1360 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1361 pattern, targetPxlSize, p);
1363 XFreeFontNames(fonts);
1370 EnableNamedMenuItem (char *menuRef, int state)
1372 MenuItem *item = MenuNameToItem(menuRef);
1374 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1378 EnableButtonBar (int state)
1381 XtSetSensitive(optList[W_BUTTON].handle, state);
1387 SetMenuEnables (Enables *enab)
1389 while (enab->name != NULL) {
1390 EnableNamedMenuItem(enab->name, enab->value);
1395 gboolean KeyPressProc(window, eventkey, data)
1397 GdkEventKey *eventkey;
1401 MoveTypeInProc(eventkey); // pop up for typed in moves
1404 /* check for other key values */
1405 switch(eventkey->keyval) {
1417 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1418 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1420 if(*nprms == 0) return;
1421 item = MenuNameToItem(prms[0]);
1422 if(item) ((MenuProc *) item->proc) ();
1436 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1437 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1438 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1439 dmEnables[i].piece);
1440 XtSetSensitive(entry, p != NULL || !appData.testLegality
1441 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1442 && !appData.icsActive));
1444 while (p && *p++ == dmEnables[i].piece) count++;
1445 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1447 XtSetArg(args[j], XtNlabel, label); j++;
1448 XtSetValues(entry, args, j);
1454 do_flash_delay (unsigned long msec)
1460 FlashDelay (int flash_delay)
1462 if(flash_delay) do_flash_delay(flash_delay);
1466 Fraction (int x, int start, int stop)
1468 double f = ((double) x - start)/(stop - start);
1469 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1473 static WindowPlacement wpNew;
1476 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1478 int touch=0, fudge = 2, f = 2;
1479 GetActualPlacement(sh, wp);
1480 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1481 if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x) < fudge) touch = 2; else // left touch
1482 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1483 if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y) < fudge) touch = 4; // top touch
1484 //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);
1485 if(!touch ) return; // only windows that touch co-move
1486 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1487 int heightInc = wpNew.height - wpMain.height;
1488 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1489 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1490 wp->y += fracTop * heightInc;
1491 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1493 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1495 wp->height += heightInc;
1496 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1497 int widthInc = wpNew.width - wpMain.width;
1498 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1499 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1500 wp->y += fracLeft * widthInc;
1501 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1503 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1505 wp->width += widthInc;
1507 wp->x += wpNew.x - wpMain.x;
1508 wp->y += wpNew.y - wpMain.y;
1509 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1510 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1512 XtSetArg(args[j], XtNx, wp->x); j++;
1513 XtSetArg(args[j], XtNy, wp->y); j++;
1514 XtSetValues(sh, args, j);
1516 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1517 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1518 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1522 ReSize (WindowPlacement *wp)
1525 int sqx, sqy, w, h, hc, lg = lineGap;
1526 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1527 hc = a.height; // clock height can depend on single / double line clock text!
1528 if(clockKludge == a.height) return; // wait for clock to get final size at startup
1529 if(clockKludge) { // clock height OK now; calculate desired initial board height
1531 wp->height = BOARD_HEIGHT * (squareSize + lineGap) + lineGap + marginH + hc;
1533 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1534 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1535 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1536 if(sqy < sqx) sqx = sqy;
1537 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1538 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1539 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1540 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1541 if(sqy < sqx) sqx = sqy;
1543 if(sqx != squareSize) {
1544 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1545 squareSize = sqx; // adopt new square size
1546 CreatePNGPieces(); // make newly scaled pieces
1547 InitDrawingSizes(0, 0); // creates grid etc.
1548 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1549 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1550 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1551 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1552 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1555 static guint delayedDragTag = 0;
1564 // GetActualPlacement(shellWidget, &wpNew);
1565 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1566 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1567 busy = 0; return; // false alarm
1570 if(appData.useStickyWindows) {
1571 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1572 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1573 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1574 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1575 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1578 DrawPosition(True, NULL);
1579 if(delayedDragTag) g_source_remove(delayedDragTag);
1580 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1587 //printf("old timr = %d\n", delayedDragTag);
1588 if(delayedDragTag) g_source_remove(delayedDragTag);
1589 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1590 //printf("new timr = %d\n", delayedDragTag);
1594 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1596 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1598 wpNew.x = event->configure.x;
1599 wpNew.y = event->configure.y;
1600 wpNew.width = event->configure.width;
1601 wpNew.height = event->configure.height;
1602 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1608 /* Disable all user input other than deleting the window */
1609 static int frozen = 0;
1615 /* Grab by a widget that doesn't accept input */
1616 gtk_grab_add(optList[W_MESSG].handle);
1620 /* Undo a FreezeUI */
1624 if (!frozen) return;
1625 gtk_grab_remove(optList[W_MESSG].handle);
1632 static int oldPausing = FALSE;
1633 static GameMode oldmode = (GameMode) -1;
1635 if (!boardWidget) return;
1637 if (pausing != oldPausing) {
1638 oldPausing = pausing;
1639 MarkMenuItem("Mode.Pause", pausing);
1641 if (appData.showButtonBar) {
1642 /* Always toggle, don't set. Previous code messes up when
1643 invoked while the button is pressed, as releasing it
1644 toggles the state again. */
1646 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1647 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1651 wname = ModeToWidgetName(oldmode);
1652 if (wname != NULL) {
1653 MarkMenuItem(wname, False);
1655 wname = ModeToWidgetName(gameMode);
1656 if (wname != NULL) {
1657 MarkMenuItem(wname, True);
1660 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1662 /* Maybe all the enables should be handled here, not just this one */
1663 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1665 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1670 * Button/menu procedures
1673 void CopyFileToClipboard(gchar *filename)
1675 gchar *selection_tmp;
1679 FILE* f = fopen(filename, "r");
1682 if (f == NULL) return;
1686 selection_tmp = g_try_malloc(len + 1);
1687 if (selection_tmp == NULL) {
1688 printf("Malloc failed in CopyFileToClipboard\n");
1691 count = fread(selection_tmp, 1, len, f);
1694 g_free(selection_tmp);
1697 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1699 // copy selection_tmp to clipboard
1700 GdkDisplay *gdisp = gdk_display_get_default();
1702 g_free(selection_tmp);
1705 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1706 gtk_clipboard_set_text(cb, selection_tmp, -1);
1707 g_free(selection_tmp);
1711 CopySomething (char *src)
1713 GdkDisplay *gdisp = gdk_display_get_default();
1715 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1716 if (gdisp == NULL) return;
1717 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1718 gtk_clipboard_set_text(cb, src, -1);
1722 PastePositionProc ()
1724 GdkDisplay *gdisp = gdk_display_get_default();
1728 if (gdisp == NULL) return;
1729 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1730 fenstr = gtk_clipboard_wait_for_text(cb);
1731 if (fenstr==NULL) return; // nothing had been selected to copy
1732 EditPositionPasteFEN(fenstr);
1744 // get game from clipboard
1745 GdkDisplay *gdisp = gdk_display_get_default();
1746 if (gdisp == NULL) return;
1747 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1748 text = gtk_clipboard_wait_for_text(cb);
1749 if (text == NULL) return; // nothing to paste
1752 // write to temp file
1753 if (text == NULL || len == 0) {
1754 return; //nothing to paste
1756 f = fopen(gamePasteFilename, "w");
1758 DisplayError(_("Can't open temp file"), errno);
1761 fwrite(text, 1, len, f);
1765 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1772 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1778 void MoveTypeInProc(eventkey)
1779 GdkEventKey *eventkey;
1783 // ingnore if ctrl, alt, or meta is pressed
1784 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1788 buf[0]=eventkey->keyval;
1790 if (eventkey->keyval > 32 && eventkey->keyval < 256)
1791 ConsoleAutoPopUp (buf);
1796 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1798 if (!TempBackwardActive) {
1799 TempBackwardActive = True;
1805 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1807 /* Check to see if triggered by a key release event for a repeating key.
1808 * If so the next queued event will be a key press of the same key at the same time */
1809 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1811 XPeekEvent(xDisplay, &next);
1812 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1813 next.xkey.keycode == event->xkey.keycode)
1817 TempBackwardActive = False;
1823 { // called from menu
1826 snprintf(buf, MSG_SIZ, "%s ./man.command", appData.sysOpen);
1829 system("xterm -e man xboard &");
1834 SetWindowTitle (char *text, char *title, char *icon)
1839 if (appData.titleInWindow) {
1841 XtSetArg(args[i], XtNlabel, text); i++;
1842 XtSetValues(titleWidget, args, i);
1845 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1846 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1847 XtSetValues(shellWidget, args, i);
1848 XSync(xDisplay, False);
1850 if (appData.titleInWindow) {
1851 SetWidgetLabel(titleWidget, text);
1853 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1858 DisplayIcsInteractionTitle (String message)
1861 if (oldICSInteractionTitle == NULL) {
1862 /* Magic to find the old window title, adapted from vim */
1863 char *wina = getenv("WINDOWID");
1865 Window win = (Window) atoi(wina);
1866 Window root, parent, *children;
1867 unsigned int nchildren;
1868 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1870 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1871 if (!XQueryTree(xDisplay, win, &root, &parent,
1872 &children, &nchildren)) break;
1873 if (children) XFree((void *)children);
1874 if (parent == root || parent == 0) break;
1877 XSetErrorHandler(oldHandler);
1879 if (oldICSInteractionTitle == NULL) {
1880 oldICSInteractionTitle = "xterm";
1883 printf("\033]0;%s\007", message);
1890 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1892 GtkWidget *w = (GtkWidget *) opt->handle;
1899 strcpy(bgcolor, "black");
1900 strcpy(fgcolor, "white");
1902 strcpy(bgcolor, "white");
1903 strcpy(fgcolor, "black");
1906 appData.lowTimeWarning &&
1907 (timer / 1000) < appData.icsAlarmTime) {
1908 strcpy(fgcolor, appData.lowTimeWarningColor);
1911 gdk_color_parse( bgcolor, &col );
1912 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1914 if (appData.clockMode) {
1915 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1916 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1918 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
1919 bgcolor, fgcolor, color);
1921 gtk_label_set_markup(GTK_LABEL(w), markup);
1925 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1928 SetClockIcon (int color)
1930 GdkPixbuf *pm = *clockIcons[color];
1931 if (mainwindowIcon != pm) {
1932 mainwindowIcon = pm;
1933 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1937 #define INPUT_SOURCE_BUF_SIZE 8192
1946 char buf[INPUT_SOURCE_BUF_SIZE];
1951 DoInputCallback(io, cond, data)
1956 /* read input from one of the input source (for example a chess program, ICS, etc).
1957 * and call a function that will handle the input
1964 /* All information (callback function, file descriptor, etc) is
1965 * saved in an InputSource structure
1967 InputSource *is = (InputSource *) data;
1969 if (is->lineByLine) {
1970 count = read(is->fd, is->unused,
1971 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1973 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1976 is->unused += count;
1978 /* break input into lines and call the callback function on each
1981 while (p < is->unused) {
1982 q = memchr(p, '\n', is->unused - p);
1983 if (q == NULL) break;
1985 (is->func)(is, is->closure, p, q - p, 0);
1988 /* remember not yet used part of the buffer */
1990 while (p < is->unused) {
1995 /* read maximum length of input buffer and send the whole buffer
1996 * to the callback function
1998 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2003 (is->func)(is, is->closure, is->buf, count, error);
2005 return True; // Must return true or the watch will be removed
2008 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2015 GIOChannel *channel;
2016 ChildProc *cp = (ChildProc *) pr;
2018 is = (InputSource *) calloc(1, sizeof(InputSource));
2019 is->lineByLine = lineByLine;
2023 is->fd = fileno(stdin);
2025 is->kind = cp->kind;
2026 is->fd = cp->fdFrom;
2029 is->unused = is->buf;
2033 /* GTK-TODO: will this work on windows?*/
2035 channel = g_io_channel_unix_new(is->fd);
2036 g_io_channel_set_close_on_unref (channel, TRUE);
2037 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2039 is->closure = closure;
2040 return (InputSourceRef) is;
2045 RemoveInputSource(isr)
2048 InputSource *is = (InputSource *) isr;
2050 if (is->sid == 0) return;
2051 g_source_remove(is->sid);
2058 static Boolean frameWaiting;
2061 FrameAlarm (int sig)
2063 frameWaiting = False;
2064 /* In case System-V style signals. Needed?? */
2065 signal(SIGALRM, FrameAlarm);
2069 FrameDelay (int time)
2071 struct itimerval delay;
2074 frameWaiting = True;
2075 signal(SIGALRM, FrameAlarm);
2076 delay.it_interval.tv_sec =
2077 delay.it_value.tv_sec = time / 1000;
2078 delay.it_interval.tv_usec =
2079 delay.it_value.tv_usec = (time % 1000) * 1000;
2080 setitimer(ITIMER_REAL, &delay, NULL);
2081 while (frameWaiting) pause();
2082 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2083 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2084 setitimer(ITIMER_REAL, &delay, NULL);
2091 FrameDelay (int time)
2094 XSync(xDisplay, False);
2096 // gtk_main_iteration_do(False);
2099 usleep(time * 1000);
2105 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2107 char buf[MSG_SIZ], *logoName = buf;
2108 if(appData.logo[n][0]) {
2109 logoName = appData.logo[n];
2110 } else if(appData.autoLogo) {
2111 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2112 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2113 } else if(appData.directory[n] && appData.directory[n][0]) {
2114 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2118 { ASSIGN(cps->programLogo, logoName); }
2122 UpdateLogos (int displ)
2124 if(optList[W_WHITE-1].handle == NULL) return;
2125 LoadLogo(&first, 0, 0);
2126 LoadLogo(&second, 1, appData.icsActive);
2127 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2131 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2142 GtkFileFilter *gtkfilter;
2143 GtkFileFilter *gtkfilter_all;
2145 char fileext[10] = "";
2146 char *result = NULL;
2149 /* make a copy of the filter string, so that strtok can work with it*/
2150 cp = strdup(filter);
2152 /* add filters for file extensions */
2153 gtkfilter = gtk_file_filter_new();
2154 gtkfilter_all = gtk_file_filter_new();
2156 /* one filter to show everything */
2157 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2158 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2160 /* add filter if present */
2161 result = strtok(cp, space);
2162 while( result != NULL ) {
2163 snprintf(fileext,10,"*%s",result);
2164 result = strtok( NULL, space );
2165 gtk_file_filter_add_pattern(gtkfilter, fileext);
2168 /* second filter to only show what's useful */
2169 gtk_file_filter_set_name (gtkfilter,filter);
2171 if (openMode[0] == 'r')
2173 dialog = gtk_file_chooser_dialog_new (label,
2175 GTK_FILE_CHOOSER_ACTION_OPEN,
2176 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2177 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2182 dialog = gtk_file_chooser_dialog_new (label,
2184 GTK_FILE_CHOOSER_ACTION_SAVE,
2185 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2186 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2188 /* add filename suggestions */
2189 if (strlen(def) > 0 )
2190 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2192 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2196 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2197 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2198 /* activate filter */
2199 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2201 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2206 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2209 f = fopen(filename, openMode);
2212 DisplayError(_("Failed to open file"), errno);
2216 /* TODO add indec */
2218 ASSIGN(*name, filename);
2219 ScheduleDelayedEvent(DelayedLoad, 50);
2224 gtk_widget_destroy (dialog);