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 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 OSx being be mistaken for option name
173 // (price is that we won't recognize Windows option format anymore).
175 // redefine some defaults
178 # define ICS_LOGON "Library/Preferences/XboardICS.conf"
179 # define SYSCONFDIR "../etc"
188 #define usleep(t) _sleep2(((t)+500)/1000)
192 # define _(s) gettext (s)
193 # define N_(s) gettext_noop (s)
199 int main P((int argc, char **argv));
200 RETSIGTYPE CmailSigHandler P((int sig));
201 RETSIGTYPE IntSigHandler P((int sig));
202 RETSIGTYPE TermSizeSigHandler P((int sig));
204 char *InsertPxlSize P((char *pattern, int targetPxlSize));
205 XFontSet CreateFontSet P((char *base_fnt_lst));
207 char *FindFont P((char *pattern, int targetPxlSize));
209 void DelayedDrag P((void));
210 void ICSInputBoxPopUp P((void));
211 void MoveTypeInProc P((GdkEventKey *eventkey));
212 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
213 Boolean TempBackwardActive = False;
214 void DisplayMove P((int moveNumber));
215 void update_ics_width P(());
216 int CopyMemoProc P(());
217 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
221 XFontSet fontSet, clockFontSet;
224 XFontStruct *clockFontStruct;
226 Font coordFontID, countFontID;
227 XFontStruct *coordFontStruct, *countFontStruct;
229 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
230 GtkWidget *mainwindow;
232 Option *optList; // contains all widgets of main window
235 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
238 static GdkPixbuf *mainwindowIcon=NULL;
239 static GdkPixbuf *WhiteIcon=NULL;
240 static GdkPixbuf *BlackIcon=NULL;
242 /* key board accelerators */
243 GtkAccelGroup *GtkAccelerators;
245 typedef unsigned int BoardSize;
247 Boolean chessProgram;
249 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
250 int smallLayout = 0, tinyLayout = 0,
251 marginW, marginH, // [HGM] for run-time resizing
252 fromX = -1, fromY = -1, toX, toY, commentUp = False,
253 errorExitStatus = -1, defaultLineGap;
255 Dimension textHeight;
257 char *chessDir, *programName, *programVersion;
258 Boolean alwaysOnTop = False;
259 char *icsTextMenuString;
261 char *firstChessProgramNames;
262 char *secondChessProgramNames;
264 WindowPlacement wpMain;
265 WindowPlacement wpConsole;
266 WindowPlacement wpComment;
267 WindowPlacement wpMoveHistory;
268 WindowPlacement wpEvalGraph;
269 WindowPlacement wpEngineOutput;
270 WindowPlacement wpGameList;
271 WindowPlacement wpTags;
272 WindowPlacement wpDualBoard;
274 /* This magic number is the number of intermediate frames used
275 in each half of the animation. For short moves it's reduced
276 by 1. The total number of frames will be factor * 2 + 1. */
279 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
286 DropMenuEnables dmEnables[] = {
295 XtResource clientResources[] = {
296 { "flashCount", "flashCount", XtRInt, sizeof(int),
297 XtOffset(AppDataPtr, flashCount), XtRImmediate,
298 (XtPointer) FLASH_COUNT },
302 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
303 char globalTranslations[] =
304 ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
305 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
306 :<KeyDown>Return: TempBackwardProc() \n \
307 :<KeyUp>Return: TempForwardProc() \n";
309 char ICSInputTranslations[] =
310 "<Key>Up: UpKeyProc() \n "
311 "<Key>Down: DownKeyProc() \n "
312 "<Key>Return: EnterKeyProc() \n";
314 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
315 // as the widget is destroyed before the up-click can call extend-end
316 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
319 String xboardResources[] = {
320 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
328 gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
331 //---------------------------------------------------------------------------------------------------------
332 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
335 #define CW_USEDEFAULT (1<<31)
336 #define ICS_TEXT_MENU_SIZE 90
337 #define DEBUG_FILE "xboard.debug"
338 #define SetCurrentDirectory chdir
339 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
343 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
346 // front-end part of option handling
348 // [HGM] This platform-dependent table provides the location for storing the color info
349 extern char *crWhite, * crBlack;
353 &appData.whitePieceColor,
354 &appData.blackPieceColor,
355 &appData.lightSquareColor,
356 &appData.darkSquareColor,
357 &appData.highlightSquareColor,
358 &appData.premoveHighlightColor,
359 &appData.lowTimeWarningColor,
370 // [HGM] font: keep a font for each square size, even non-stndard ones
373 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
374 char *fontTable[NUM_FONTS][MAX_SIZE];
377 ParseFont (char *name, int number)
378 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
380 if(sscanf(name, "size%d:", &size)) {
381 // [HGM] font: font is meant for specific boardSize (likely from settings file);
382 // defer processing it until we know if it matches our board size
383 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
384 fontTable[number][size] = strdup(strchr(name, ':')+1);
385 fontValid[number][size] = True;
390 case 0: // CLOCK_FONT
391 appData.clockFont = strdup(name);
393 case 1: // MESSAGE_FONT
394 appData.font = strdup(name);
396 case 2: // COORD_FONT
397 appData.coordFont = strdup(name);
402 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
407 { // only 2 fonts currently
408 appData.clockFont = CLOCK_FONT_NAME;
409 appData.coordFont = COORD_FONT_NAME;
410 appData.font = DEFAULT_FONT_NAME;
415 { // no-op, until we identify the code for this already in XBoard and move it here
419 ParseColor (int n, char *name)
420 { // in XBoard, just copy the color-name string
421 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
425 ParseTextAttribs (ColorClass cc, char *s)
427 (&appData.colorShout)[cc] = strdup(s);
431 ParseBoardSize (void *addr, char *name)
433 appData.boardSize = strdup(name);
438 { // In XBoard the sound-playing program takes care of obtaining the actual sound
442 SetCommPortDefaults ()
443 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
446 // [HGM] args: these three cases taken out to stay in front-end
448 SaveFontArg (FILE *f, ArgDescriptor *ad)
451 int i, n = (int)(intptr_t)ad->argLoc;
453 case 0: // CLOCK_FONT
454 name = appData.clockFont;
456 case 1: // MESSAGE_FONT
459 case 2: // COORD_FONT
460 name = appData.coordFont;
465 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
466 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
467 fontTable[n][squareSize] = strdup(name);
468 fontValid[n][squareSize] = True;
471 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
472 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
477 { // nothing to do, as the sounds are at all times represented by their text-string names already
481 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
482 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
483 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
487 SaveColor (FILE *f, ArgDescriptor *ad)
488 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
489 if(colorVariable[(int)(intptr_t)ad->argLoc])
490 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
494 SaveBoardSize (FILE *f, char *name, void *addr)
495 { // wrapper to shield back-end from BoardSize & sizeInfo
496 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
500 ParseCommPortSettings (char *s)
501 { // no such option in XBoard (yet)
507 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
511 gtk_widget_get_allocation(shell, &a);
512 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
516 wp->height = a.height;
517 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
518 frameX = 3; frameY = 3; // remember to decide if windows touch
523 { // wrapper to shield use of window handles from back-end (make addressible by number?)
524 // In XBoard this will have to wait until awareness of window parameters is implemented
525 GetActualPlacement(shellWidget, &wpMain);
526 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
527 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
528 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
529 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
530 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
531 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
535 PrintCommPortSettings (FILE *f, char *name)
536 { // This option does not exist in XBoard
540 EnsureOnScreen (int *x, int *y, int minX, int minY)
547 { // [HGM] args: allows testing if main window is realized from back-end
548 return DialogExists(BoardWindow);
552 PopUpStartupDialog ()
553 { // start menu not implemented in XBoard
557 ConvertToLine (int argc, char **argv)
559 static char line[128*1024], buf[1024];
563 for(i=1; i<argc; i++)
565 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
566 && argv[i][0] != '{' )
567 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
569 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
570 strncat(line, buf, 128*1024 - strlen(line) - 1 );
573 line[strlen(line)-1] = NULLCHAR;
577 //--------------------------------------------------------------------------------------------
582 ResizeBoardWindow (int w, int h, int inhibit)
585 if(clockKludge) return; // ignore as long as clock does not have final height
586 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
587 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
588 h += marginH + a.height + 1;
589 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
594 { // dummy, as the GTK code does not make colors in advance
599 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
600 { // determine what fonts to use, and create them
605 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
606 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
607 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
608 appData.font = fontTable[MESSAGE_FONT][squareSize];
609 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
610 appData.coordFont = fontTable[COORD_FONT][squareSize];
613 appData.font = InsertPxlSize(appData.font, fontPxlSize);
614 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
615 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
616 fontSet = CreateFontSet(appData.font);
617 clockFontSet = CreateFontSet(appData.clockFont);
619 /* For the coordFont, use the 0th font of the fontset. */
620 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
621 XFontStruct **font_struct_list;
622 XFontSetExtents *fontSize;
623 char **font_name_list;
624 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
625 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
626 coordFontStruct = XQueryFont(xDisplay, coordFontID);
627 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
628 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
631 appData.font = FindFont(appData.font, fontPxlSize);
632 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
633 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
634 clockFontID = XLoadFont(xDisplay, appData.clockFont);
635 clockFontStruct = XQueryFont(xDisplay, clockFontID);
636 coordFontID = XLoadFont(xDisplay, appData.coordFont);
637 coordFontStruct = XQueryFont(xDisplay, coordFontID);
638 // textHeight in !NLS mode!
640 countFontID = coordFontID; // [HGM] holdings
641 countFontStruct = coordFontStruct;
643 xdb = XtDatabase(xDisplay);
645 XrmPutLineResource(&xdb, "*international: True");
646 vTo.size = sizeof(XFontSet);
647 vTo.addr = (XtPointer) &fontSet;
648 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
650 XrmPutStringResource(&xdb, "*font", appData.font);
661 case ArgInt: p = " N"; break;
662 case ArgString: p = " STR"; break;
663 case ArgBoolean: p = " TF"; break;
664 case ArgSettingsFilename:
665 case ArgBackupSettingsFile:
666 case ArgFilename: p = " FILE"; break;
667 case ArgX: p = " Nx"; break;
668 case ArgY: p = " Ny"; break;
669 case ArgAttribs: p = " TEXTCOL"; break;
670 case ArgColor: p = " COL"; break;
671 case ArgFont: p = " FONT"; break;
672 case ArgBoardSize: p = " SIZE"; break;
673 case ArgFloat: p = " FLOAT"; break;
678 case ArgCommSettings:
689 ArgDescriptor *q, *p = argDescriptors+5;
690 printf("\nXBoard accepts the following options:\n"
691 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
692 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
693 " SIZE = board-size spec(s)\n"
694 " Within parentheses are short forms, or options to set to true or false.\n"
695 " Persistent options (saved in the settings file) are marked with *)\n\n");
697 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
698 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
699 if(p->save) strcat(buf+len, "*");
700 for(q=p+1; q->argLoc == p->argLoc; q++) {
701 if(q->argName[0] == '-') continue;
702 strcat(buf+len, q == p+1 ? " (" : " ");
703 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
705 if(q != p+1) strcat(buf+len, ")");
707 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
710 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
714 SlaveResize (Option *opt)
716 static int slaveW, slaveH, w, h;
719 gtk_widget_get_allocation(shells[DummyDlg], &a);
720 w = a.width; h = a.height;
721 gtk_widget_get_allocation(opt->handle, &a);
722 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
723 slaveH = h - a.height + 13;
725 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
729 static char clickedFile[MSG_SIZ];
733 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
734 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
735 if(suppress) { // we just started XBoard without arguments
736 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
737 } else { // we are running something presumably useful
739 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
740 system(buf); // start new instance on this file
747 main (int argc, char **argv)
749 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
750 int boardWidth, boardHeight, w, h;
752 int forceMono = False;
754 srandom(time(0)); // [HGM] book: make random truly random
756 setbuf(stdout, NULL);
757 setbuf(stderr, NULL);
760 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
761 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
765 if(argc > 1 && !strcmp(argv[1], "--help" )) {
771 gtk_init (&argc, &argv);
773 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
774 GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
775 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
776 // we must call application ready before we can get the signal,
777 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
778 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
779 gtkosx_application_ready(theApp);
780 suppress = (argc == 1 || argc > 1 && argv[1][00] != '-'); // OSX sends signal even if name was already argv[1]!
781 if(argc == 1) { // called without args: OSX open-file signal might follow
782 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
783 usleep(10000); // wait 10 msec (and hope this is long enough).
784 while(gtk_events_pending())
785 gtk_main_iteration(); // process all events that came in upto now
786 suppress = 0; // future open-file signals should start new instance
787 if(clickedFile[0]) { // we were sent an open-file signal with filename!
788 fakeArgv[0] = argv[0];
789 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
795 /* set up keyboard accelerators group */
796 GtkAccelerators = gtk_accel_group_new();
798 programName = strrchr(argv[0], '/');
799 if (programName == NULL)
800 programName = argv[0];
805 // if (appData.debugMode) {
806 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
809 bindtextdomain(PACKAGE, LOCALEDIR);
810 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
814 appData.boardSize = "";
815 InitAppData(ConvertToLine(argc, argv));
817 if (p == NULL) p = "/tmp";
818 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
819 gameCopyFilename = (char*) malloc(i);
820 gamePasteFilename = (char*) malloc(i);
821 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
822 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
824 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
825 static char buf[MSG_SIZ];
826 EscapeExpand(buf, appData.firstInitString);
827 appData.firstInitString = strdup(buf);
828 EscapeExpand(buf, appData.secondInitString);
829 appData.secondInitString = strdup(buf);
830 EscapeExpand(buf, appData.firstComputerString);
831 appData.firstComputerString = strdup(buf);
832 EscapeExpand(buf, appData.secondComputerString);
833 appData.secondComputerString = strdup(buf);
836 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
839 if (chdir(chessDir) != 0) {
840 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
846 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
847 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
848 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
849 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
852 setbuf(debugFP, NULL);
856 if (appData.debugMode) {
857 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
861 /* [HGM,HR] make sure board size is acceptable */
862 if(appData.NrFiles > BOARD_FILES ||
863 appData.NrRanks > BOARD_RANKS )
864 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
867 /* This feature does not work; animation needs a rewrite */
868 appData.highlightDragging = FALSE;
872 gameInfo.variant = StringToVariant(appData.variant);
876 * determine size, based on supplied or remembered -size, or screen size
878 if (isdigit(appData.boardSize[0])) {
879 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
880 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
881 &fontPxlSize, &smallLayout, &tinyLayout);
883 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
884 programName, appData.boardSize);
888 /* Find some defaults; use the nearest known size */
889 SizeDefaults *szd, *nearest;
890 int distance = 99999;
891 nearest = szd = sizeDefaults;
892 while (szd->name != NULL) {
893 if (abs(szd->squareSize - squareSize) < distance) {
895 distance = abs(szd->squareSize - squareSize);
896 if (distance == 0) break;
900 if (i < 2) lineGap = nearest->lineGap;
901 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
902 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
903 if (i < 5) fontPxlSize = nearest->fontPxlSize;
904 if (i < 6) smallLayout = nearest->smallLayout;
905 if (i < 7) tinyLayout = nearest->tinyLayout;
908 SizeDefaults *szd = sizeDefaults;
909 if (*appData.boardSize == NULLCHAR) {
910 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
911 guint screenwidth = gdk_screen_get_width(screen);
912 guint screenheight = gdk_screen_get_height(screen);
913 while (screenwidth < szd->minScreenSize ||
914 screenheight < szd->minScreenSize) {
917 if (szd->name == NULL) szd--;
918 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
920 while (szd->name != NULL &&
921 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
922 if (szd->name == NULL) {
923 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
924 programName, appData.boardSize);
928 squareSize = szd->squareSize;
929 lineGap = szd->lineGap;
930 clockFontPxlSize = szd->clockFontPxlSize;
931 coordFontPxlSize = szd->coordFontPxlSize;
932 fontPxlSize = szd->fontPxlSize;
933 smallLayout = szd->smallLayout;
934 tinyLayout = szd->tinyLayout;
935 // [HGM] font: use defaults from settings file if available and not overruled
938 defaultLineGap = lineGap;
939 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
941 /* [HR] height treated separately (hacked) */
942 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
943 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
946 * Determine what fonts to use.
949 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
953 * Detect if there are not enough colors available and adapt.
956 if (DefaultDepth(xDisplay, xScreen) <= 2) {
957 appData.monoMode = True;
961 forceMono = MakeColors();
964 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
966 appData.monoMode = True;
969 ParseIcsTextColors();
975 layoutName = "tinyLayout";
976 } else if (smallLayout) {
977 layoutName = "smallLayout";
979 layoutName = "normalLayout";
982 wpMain.width = -1; // prevent popup sizes window
983 optList = BoardPopUp(squareSize, lineGap, (void*)
993 InitDrawingHandle(optList + W_BOARD);
994 shellWidget = shells[BoardWindow];
995 currBoard = &optList[W_BOARD];
996 boardWidget = optList[W_BOARD].handle;
997 menuBarWidget = optList[W_MENU].handle;
998 dropMenu = optList[W_DROP].handle;
999 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1001 formWidget = XtParent(boardWidget);
1002 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1003 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1004 XtGetValues(optList[W_WHITE].handle, args, 2);
1005 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1006 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1007 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1008 XtGetValues(optList[W_PAUSE].handle, args, 2);
1012 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1013 // not need to go into InitDrawingSizes().
1017 // add accelerators to main shell
1018 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1021 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1023 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
1024 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
1025 mainwindowIcon = WhiteIcon;
1026 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1030 * Create a cursor for the board widget.
1033 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1034 XChangeWindowAttributes(xDisplay, xBoardWindow,
1035 CWCursor, &window_attributes);
1039 * Inhibit shell resizing.
1042 shellArgs[0].value = (XtArgVal) &w;
1043 shellArgs[1].value = (XtArgVal) &h;
1044 XtGetValues(shellWidget, shellArgs, 2);
1045 shellArgs[4].value = shellArgs[2].value = w;
1046 shellArgs[5].value = shellArgs[3].value = h;
1047 // XtSetValues(shellWidget, &shellArgs[2], 4);
1050 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1051 // It wil only become known asynchronously, when we first write a string into it.
1052 // This will then change the clock widget height, which triggers resizing the top-level window
1053 // and a configure event. Only then can we know the total height of the top-level window,
1054 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1055 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1058 gtk_widget_get_allocation(shells[BoardWindow], &a);
1059 w = a.width; h = a.height;
1060 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1061 clockKludge = hc = a.height;
1062 gtk_widget_get_allocation(boardWidget, &a);
1063 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1064 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1070 if(appData.logoSize)
1071 { // locate and read user logo
1073 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1074 ASSIGN(userLogo, buf);
1077 if (appData.animate || appData.animateDragging)
1080 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1081 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1083 /* [AS] Restore layout */
1084 if( wpMoveHistory.visible ) {
1088 if( wpEvalGraph.visible )
1093 if( wpEngineOutput.visible ) {
1094 EngineOutputPopUp();
1099 if (errorExitStatus == -1) {
1100 if (appData.icsActive) {
1101 /* We now wait until we see "login:" from the ICS before
1102 sending the logon script (problems with timestamp otherwise) */
1103 /*ICSInitScript();*/
1104 if (appData.icsInputBox) ICSInputBoxPopUp();
1108 signal(SIGWINCH, TermSizeSigHandler);
1110 signal(SIGINT, IntSigHandler);
1111 signal(SIGTERM, IntSigHandler);
1112 if (*appData.cmailGameName != NULLCHAR) {
1113 signal(SIGUSR1, CmailSigHandler);
1117 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1120 // XtSetKeyboardFocus(shellWidget, formWidget);
1122 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1125 /* check for GTK events and process them */
1128 gtk_main_iteration();
1131 if (appData.debugMode) fclose(debugFP); // [DM] debug
1136 TermSizeSigHandler (int sig)
1142 IntSigHandler (int sig)
1148 CmailSigHandler (int sig)
1153 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1155 /* Activate call-back function CmailSigHandlerCallBack() */
1156 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1158 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1162 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1165 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1167 /**** end signal code ****/
1170 #define Abs(n) ((n)<0 ? -(n) : (n))
1174 InsertPxlSize (char *pattern, int targetPxlSize)
1176 char *base_fnt_lst, strInt[12], *p, *q;
1177 int alternatives, i, len, strIntLen;
1180 * Replace the "*" (if present) in the pixel-size slot of each
1181 * alternative with the targetPxlSize.
1185 while ((p = strchr(p, ',')) != NULL) {
1189 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1190 strIntLen = strlen(strInt);
1191 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1195 while (alternatives--) {
1196 char *comma = strchr(p, ',');
1197 for (i=0; i<14; i++) {
1198 char *hyphen = strchr(p, '-');
1200 if (comma && hyphen > comma) break;
1201 len = hyphen + 1 - p;
1202 if (i == 7 && *p == '*' && len == 2) {
1204 memcpy(q, strInt, strIntLen);
1214 len = comma + 1 - p;
1221 return base_fnt_lst;
1226 CreateFontSet (char *base_fnt_lst)
1229 char **missing_list;
1233 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1234 &missing_list, &missing_count, &def_string);
1235 if (appData.debugMode) {
1237 XFontStruct **font_struct_list;
1238 char **font_name_list;
1239 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1241 fprintf(debugFP, " got list %s, locale %s\n",
1242 XBaseFontNameListOfFontSet(fntSet),
1243 XLocaleOfFontSet(fntSet));
1244 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1245 for (i = 0; i < count; i++) {
1246 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1249 for (i = 0; i < missing_count; i++) {
1250 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1253 if (fntSet == NULL) {
1254 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1260 #else // not ENABLE_NLS
1262 * Find a font that matches "pattern" that is as close as
1263 * possible to the targetPxlSize. Prefer fonts that are k
1264 * pixels smaller to fonts that are k pixels larger. The
1265 * pattern must be in the X Consortium standard format,
1266 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1267 * The return value should be freed with XtFree when no
1271 FindFont (char *pattern, int targetPxlSize)
1273 char **fonts, *p, *best, *scalable, *scalableTail;
1274 int i, j, nfonts, minerr, err, pxlSize;
1277 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1279 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1280 programName, pattern);
1287 for (i=0; i<nfonts; i++) {
1290 if (*p != '-') continue;
1292 if (*p == NULLCHAR) break;
1293 if (*p++ == '-') j++;
1295 if (j < 7) continue;
1298 scalable = fonts[i];
1301 err = pxlSize - targetPxlSize;
1302 if (Abs(err) < Abs(minerr) ||
1303 (minerr > 0 && err < 0 && -err == minerr)) {
1309 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1310 /* If the error is too big and there is a scalable font,
1311 use the scalable font. */
1312 int headlen = scalableTail - scalable;
1313 p = (char *) XtMalloc(strlen(scalable) + 10);
1314 while (isdigit(*scalableTail)) scalableTail++;
1315 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1317 p = (char *) XtMalloc(strlen(best) + 2);
1318 safeStrCpy(p, best, strlen(best)+1 );
1320 if (appData.debugMode) {
1321 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1322 pattern, targetPxlSize, p);
1324 XFreeFontNames(fonts);
1331 EnableNamedMenuItem (char *menuRef, int state)
1333 MenuItem *item = MenuNameToItem(menuRef);
1335 if(item) gtk_widget_set_sensitive(item->handle, state);
1339 EnableButtonBar (int state)
1342 XtSetSensitive(optList[W_BUTTON].handle, state);
1348 SetMenuEnables (Enables *enab)
1350 while (enab->name != NULL) {
1351 EnableNamedMenuItem(enab->name, enab->value);
1356 gboolean KeyPressProc(window, eventkey, data)
1358 GdkEventKey *eventkey;
1362 MoveTypeInProc(eventkey); // pop up for typed in moves
1365 /* check for other key values */
1366 switch(eventkey->keyval) {
1378 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1379 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1381 if(*nprms == 0) return;
1382 item = MenuNameToItem(prms[0]);
1383 if(item) ((MenuProc *) item->proc) ();
1397 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1398 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1399 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1400 dmEnables[i].piece);
1401 XtSetSensitive(entry, p != NULL || !appData.testLegality
1402 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1403 && !appData.icsActive));
1405 while (p && *p++ == dmEnables[i].piece) count++;
1406 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1408 XtSetArg(args[j], XtNlabel, label); j++;
1409 XtSetValues(entry, args, j);
1415 do_flash_delay (unsigned long msec)
1421 FlashDelay (int flash_delay)
1423 if(flash_delay) do_flash_delay(flash_delay);
1427 Fraction (int x, int start, int stop)
1429 double f = ((double) x - start)/(stop - start);
1430 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1434 static WindowPlacement wpNew;
1437 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1439 int touch=0, fudge = 2, f = 2;
1440 GetActualPlacement(sh, wp);
1441 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1442 if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x) < fudge) touch = 2; else // left touch
1443 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1444 if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y) < fudge) touch = 4; // top touch
1445 //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);
1446 if(!touch ) return; // only windows that touch co-move
1447 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1448 int heightInc = wpNew.height - wpMain.height;
1449 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1450 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1451 wp->y += fracTop * heightInc;
1452 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1454 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1456 wp->height += heightInc;
1457 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1458 int widthInc = wpNew.width - wpMain.width;
1459 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1460 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1461 wp->y += fracLeft * widthInc;
1462 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1464 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1466 wp->width += widthInc;
1468 wp->x += wpNew.x - wpMain.x;
1469 wp->y += wpNew.y - wpMain.y;
1470 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1471 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1473 XtSetArg(args[j], XtNx, wp->x); j++;
1474 XtSetArg(args[j], XtNy, wp->y); j++;
1475 XtSetValues(sh, args, j);
1477 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1478 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1479 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1483 ReSize (WindowPlacement *wp)
1486 int sqx, sqy, w, h, hc, lg = lineGap;
1487 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1488 hc = a.height; // clock height can depend on single / double line clock text!
1489 if(clockKludge == a.height) return; // wait for clock to get final size at startup
1490 if(clockKludge) { // clock height OK now; calculate desired initial board height
1492 wp->height = BOARD_HEIGHT * (squareSize + lineGap) + lineGap + marginH + hc;
1494 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1495 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1496 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1497 if(sqy < sqx) sqx = sqy;
1498 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1499 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1500 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1501 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1502 if(sqy < sqx) sqx = sqy;
1504 if(sqx != squareSize) {
1505 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1506 squareSize = sqx; // adopt new square size
1507 CreatePNGPieces(); // make newly scaled pieces
1508 InitDrawingSizes(0, 0); // creates grid etc.
1509 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1510 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1511 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1512 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1513 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1516 static guint delayedDragTag = 0;
1525 // GetActualPlacement(shellWidget, &wpNew);
1526 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1527 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1528 busy = 0; return; // false alarm
1531 if(appData.useStickyWindows) {
1532 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1533 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1534 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1535 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1538 DrawPosition(True, NULL);
1539 if(delayedDragTag) g_source_remove(delayedDragTag);
1540 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1547 //printf("old timr = %d\n", delayedDragTag);
1548 if(delayedDragTag) g_source_remove(delayedDragTag);
1549 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1550 //printf("new timr = %d\n", delayedDragTag);
1554 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1556 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1558 wpNew.x = event->configure.x;
1559 wpNew.y = event->configure.y;
1560 wpNew.width = event->configure.width;
1561 wpNew.height = event->configure.height;
1562 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1568 /* Disable all user input other than deleting the window */
1569 static int frozen = 0;
1575 /* Grab by a widget that doesn't accept input */
1576 gtk_grab_add(optList[W_MESSG].handle);
1580 /* Undo a FreezeUI */
1584 if (!frozen) return;
1585 gtk_grab_remove(optList[W_MESSG].handle);
1592 static int oldPausing = FALSE;
1593 static GameMode oldmode = (GameMode) -1;
1595 if (!boardWidget) return;
1597 if (pausing != oldPausing) {
1598 oldPausing = pausing;
1599 MarkMenuItem("Mode.Pause", pausing);
1601 if (appData.showButtonBar) {
1602 /* Always toggle, don't set. Previous code messes up when
1603 invoked while the button is pressed, as releasing it
1604 toggles the state again. */
1606 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1607 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1611 wname = ModeToWidgetName(oldmode);
1612 if (wname != NULL) {
1613 MarkMenuItem(wname, False);
1615 wname = ModeToWidgetName(gameMode);
1616 if (wname != NULL) {
1617 MarkMenuItem(wname, True);
1620 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1622 /* Maybe all the enables should be handled here, not just this one */
1623 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1625 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1630 * Button/menu procedures
1633 void CopyFileToClipboard(gchar *filename)
1635 gchar *selection_tmp;
1639 FILE* f = fopen(filename, "r");
1642 if (f == NULL) return;
1646 selection_tmp = g_try_malloc(len + 1);
1647 if (selection_tmp == NULL) {
1648 printf("Malloc failed in CopyFileToClipboard\n");
1651 count = fread(selection_tmp, 1, len, f);
1654 g_free(selection_tmp);
1657 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1659 // copy selection_tmp to clipboard
1660 GdkDisplay *gdisp = gdk_display_get_default();
1662 g_free(selection_tmp);
1665 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1666 gtk_clipboard_set_text(cb, selection_tmp, -1);
1667 g_free(selection_tmp);
1671 CopySomething (char *src)
1673 GdkDisplay *gdisp = gdk_display_get_default();
1675 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1676 if (gdisp == NULL) return;
1677 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1678 gtk_clipboard_set_text(cb, src, -1);
1682 PastePositionProc ()
1684 GdkDisplay *gdisp = gdk_display_get_default();
1688 if (gdisp == NULL) return;
1689 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1690 fenstr = gtk_clipboard_wait_for_text(cb);
1691 if (fenstr==NULL) return; // nothing had been selected to copy
1692 EditPositionPasteFEN(fenstr);
1704 // get game from clipboard
1705 GdkDisplay *gdisp = gdk_display_get_default();
1706 if (gdisp == NULL) return;
1707 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1708 text = gtk_clipboard_wait_for_text(cb);
1709 if (text == NULL) return; // nothing to paste
1712 // write to temp file
1713 if (text == NULL || len == 0) {
1714 return; //nothing to paste
1716 f = fopen(gamePasteFilename, "w");
1718 DisplayError(_("Can't open temp file"), errno);
1721 fwrite(text, 1, len, f);
1725 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1732 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1738 void MoveTypeInProc(eventkey)
1739 GdkEventKey *eventkey;
1743 // ingnore if ctrl, alt, or meta is pressed
1744 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1748 buf[0]=eventkey->keyval;
1750 if (eventkey->keyval > 32 && eventkey->keyval < 256)
1756 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1758 if (!TempBackwardActive) {
1759 TempBackwardActive = True;
1765 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1767 /* Check to see if triggered by a key release event for a repeating key.
1768 * If so the next queued event will be a key press of the same key at the same time */
1769 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1771 XPeekEvent(xDisplay, &next);
1772 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1773 next.xkey.keycode == event->xkey.keycode)
1777 TempBackwardActive = False;
1783 { // called from menu
1785 system("%s ./man.command", appData.sysOpen);
1787 system("xterm -e man xboard &");
1792 SetWindowTitle (char *text, char *title, char *icon)
1797 if (appData.titleInWindow) {
1799 XtSetArg(args[i], XtNlabel, text); i++;
1800 XtSetValues(titleWidget, args, i);
1803 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1804 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1805 XtSetValues(shellWidget, args, i);
1806 XSync(xDisplay, False);
1808 if (appData.titleInWindow) {
1809 SetWidgetLabel(titleWidget, text);
1811 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1816 DisplayIcsInteractionTitle (String message)
1819 if (oldICSInteractionTitle == NULL) {
1820 /* Magic to find the old window title, adapted from vim */
1821 char *wina = getenv("WINDOWID");
1823 Window win = (Window) atoi(wina);
1824 Window root, parent, *children;
1825 unsigned int nchildren;
1826 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1828 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1829 if (!XQueryTree(xDisplay, win, &root, &parent,
1830 &children, &nchildren)) break;
1831 if (children) XFree((void *)children);
1832 if (parent == root || parent == 0) break;
1835 XSetErrorHandler(oldHandler);
1837 if (oldICSInteractionTitle == NULL) {
1838 oldICSInteractionTitle = "xterm";
1841 printf("\033]0;%s\007", message);
1848 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1850 GtkWidget *w = (GtkWidget *) opt->handle;
1857 strcpy(bgcolor, "black");
1858 strcpy(fgcolor, "white");
1860 strcpy(bgcolor, "white");
1861 strcpy(fgcolor, "black");
1864 appData.lowTimeWarning &&
1865 (timer / 1000) < appData.icsAlarmTime) {
1866 strcpy(fgcolor, appData.lowTimeWarningColor);
1869 gdk_color_parse( bgcolor, &col );
1870 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1872 if (appData.clockMode) {
1873 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1874 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1876 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
1877 bgcolor, fgcolor, color);
1879 gtk_label_set_markup(GTK_LABEL(w), markup);
1883 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1886 SetClockIcon (int color)
1888 GdkPixbuf *pm = *clockIcons[color];
1889 if (mainwindowIcon != pm) {
1890 mainwindowIcon = pm;
1891 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1895 #define INPUT_SOURCE_BUF_SIZE 8192
1904 char buf[INPUT_SOURCE_BUF_SIZE];
1909 DoInputCallback(io, cond, data)
1914 /* read input from one of the input source (for example a chess program, ICS, etc).
1915 * and call a function that will handle the input
1922 /* All information (callback function, file descriptor, etc) is
1923 * saved in an InputSource structure
1925 InputSource *is = (InputSource *) data;
1927 if (is->lineByLine) {
1928 count = read(is->fd, is->unused,
1929 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1931 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1934 is->unused += count;
1936 /* break input into lines and call the callback function on each
1939 while (p < is->unused) {
1940 q = memchr(p, '\n', is->unused - p);
1941 if (q == NULL) break;
1943 (is->func)(is, is->closure, p, q - p, 0);
1946 /* remember not yet used part of the buffer */
1948 while (p < is->unused) {
1953 /* read maximum length of input buffer and send the whole buffer
1954 * to the callback function
1956 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1961 (is->func)(is, is->closure, is->buf, count, error);
1963 return True; // Must return true or the watch will be removed
1966 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1973 GIOChannel *channel;
1974 ChildProc *cp = (ChildProc *) pr;
1976 is = (InputSource *) calloc(1, sizeof(InputSource));
1977 is->lineByLine = lineByLine;
1981 is->fd = fileno(stdin);
1983 is->kind = cp->kind;
1984 is->fd = cp->fdFrom;
1987 is->unused = is->buf;
1991 /* GTK-TODO: will this work on windows?*/
1993 channel = g_io_channel_unix_new(is->fd);
1994 g_io_channel_set_close_on_unref (channel, TRUE);
1995 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1997 is->closure = closure;
1998 return (InputSourceRef) is;
2003 RemoveInputSource(isr)
2006 InputSource *is = (InputSource *) isr;
2008 if (is->sid == 0) return;
2009 g_source_remove(is->sid);
2016 static Boolean frameWaiting;
2019 FrameAlarm (int sig)
2021 frameWaiting = False;
2022 /* In case System-V style signals. Needed?? */
2023 signal(SIGALRM, FrameAlarm);
2027 FrameDelay (int time)
2029 struct itimerval delay;
2032 frameWaiting = True;
2033 signal(SIGALRM, FrameAlarm);
2034 delay.it_interval.tv_sec =
2035 delay.it_value.tv_sec = time / 1000;
2036 delay.it_interval.tv_usec =
2037 delay.it_value.tv_usec = (time % 1000) * 1000;
2038 setitimer(ITIMER_REAL, &delay, NULL);
2039 while (frameWaiting) pause();
2040 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2041 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2042 setitimer(ITIMER_REAL, &delay, NULL);
2049 FrameDelay (int time)
2052 XSync(xDisplay, False);
2054 // gtk_main_iteration_do(False);
2057 usleep(time * 1000);
2063 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2065 char buf[MSG_SIZ], *logoName = buf;
2066 if(appData.logo[n][0]) {
2067 logoName = appData.logo[n];
2068 } else if(appData.autoLogo) {
2069 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2070 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2071 } else if(appData.directory[n] && appData.directory[n][0]) {
2072 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2076 { ASSIGN(cps->programLogo, logoName); }
2080 UpdateLogos (int displ)
2082 if(optList[W_WHITE-1].handle == NULL) return;
2083 LoadLogo(&first, 0, 0);
2084 LoadLogo(&second, 1, appData.icsActive);
2085 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2089 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2100 GtkFileFilter *gtkfilter;
2101 GtkFileFilter *gtkfilter_all;
2103 char fileext[10] = "";
2104 char *result = NULL;
2107 /* make a copy of the filter string, so that strtok can work with it*/
2108 cp = strdup(filter);
2110 /* add filters for file extensions */
2111 gtkfilter = gtk_file_filter_new();
2112 gtkfilter_all = gtk_file_filter_new();
2114 /* one filter to show everything */
2115 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2116 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2118 /* add filter if present */
2119 result = strtok(cp, space);
2120 while( result != NULL ) {
2121 snprintf(fileext,10,"*%s",result);
2122 result = strtok( NULL, space );
2123 gtk_file_filter_add_pattern(gtkfilter, fileext);
2126 /* second filter to only show what's useful */
2127 gtk_file_filter_set_name (gtkfilter,filter);
2129 if (openMode[0] == 'r')
2131 dialog = gtk_file_chooser_dialog_new (label,
2133 GTK_FILE_CHOOSER_ACTION_OPEN,
2134 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2135 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2140 dialog = gtk_file_chooser_dialog_new (label,
2142 GTK_FILE_CHOOSER_ACTION_SAVE,
2143 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2144 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2146 /* add filename suggestions */
2147 if (strlen(def) > 0 )
2148 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2150 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2154 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2155 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2156 /* activate filter */
2157 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2159 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2164 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2167 f = fopen(filename, openMode);
2170 DisplayError(_("Failed to open file"), errno);
2174 /* TODO add indec */
2176 ASSIGN(*name, filename);
2177 ScheduleDelayedEvent(DelayedLoad, 50);
2182 gtk_widget_destroy (dialog);