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:
690 ArgDescriptor *q, *p = argDescriptors+5;
691 printf("\nXBoard accepts the following options:\n"
692 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
693 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
694 " SIZE = board-size spec(s)\n"
695 " Within parentheses are short forms, or options to set to true or false.\n"
696 " Persistent options (saved in the settings file) are marked with *)\n\n");
698 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
699 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
700 if(p->save) strcat(buf+len, "*");
701 for(q=p+1; q->argLoc == p->argLoc; q++) {
702 if(q->argName[0] == '-') continue;
703 strcat(buf+len, q == p+1 ? " (" : " ");
704 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
706 if(q != p+1) strcat(buf+len, ")");
708 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
711 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
715 SlaveResize (Option *opt)
717 static int slaveW, slaveH, w, h;
720 gtk_widget_get_allocation(shells[DummyDlg], &a);
721 w = a.width; h = a.height;
722 gtk_widget_get_allocation(opt->handle, &a);
723 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
724 slaveH = h - a.height + 13;
726 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
730 static char clickedFile[MSG_SIZ];
734 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
735 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
736 if(suppress) { // we just started XBoard without arguments
737 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
738 } else { // we are running something presumably useful
740 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
741 system(buf); // start new instance on this file
748 main (int argc, char **argv)
750 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
751 int boardWidth, boardHeight, w, h;
753 int forceMono = False;
755 srandom(time(0)); // [HGM] book: make random truly random
757 setbuf(stdout, NULL);
758 setbuf(stderr, NULL);
761 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
762 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
766 if(argc > 1 && !strcmp(argv[1], "--help" )) {
772 gtk_init (&argc, &argv);
774 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
775 GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
776 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
777 // we must call application ready before we can get the signal,
778 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
779 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
780 gtkosx_application_ready(theApp);
781 suppress = (argc == 1 || argc > 1 && argv[1][00] != '-'); // OSX sends signal even if name was already argv[1]!
782 if(argc == 1) { // called without args: OSX open-file signal might follow
783 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
784 usleep(10000); // wait 10 msec (and hope this is long enough).
785 while(gtk_events_pending())
786 gtk_main_iteration(); // process all events that came in upto now
787 suppress = 0; // future open-file signals should start new instance
788 if(clickedFile[0]) { // we were sent an open-file signal with filename!
789 fakeArgv[0] = argv[0];
790 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
796 /* set up keyboard accelerators group */
797 GtkAccelerators = gtk_accel_group_new();
799 programName = strrchr(argv[0], '/');
800 if (programName == NULL)
801 programName = argv[0];
806 // if (appData.debugMode) {
807 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
810 bindtextdomain(PACKAGE, LOCALEDIR);
811 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
815 appData.boardSize = "";
816 InitAppData(ConvertToLine(argc, argv));
818 if (p == NULL) p = "/tmp";
819 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
820 gameCopyFilename = (char*) malloc(i);
821 gamePasteFilename = (char*) malloc(i);
822 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
823 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
825 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
826 static char buf[MSG_SIZ];
827 EscapeExpand(buf, appData.firstInitString);
828 appData.firstInitString = strdup(buf);
829 EscapeExpand(buf, appData.secondInitString);
830 appData.secondInitString = strdup(buf);
831 EscapeExpand(buf, appData.firstComputerString);
832 appData.firstComputerString = strdup(buf);
833 EscapeExpand(buf, appData.secondComputerString);
834 appData.secondComputerString = strdup(buf);
837 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
840 if (chdir(chessDir) != 0) {
841 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
847 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
848 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
849 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
850 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
853 setbuf(debugFP, NULL);
857 if (appData.debugMode) {
858 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
862 /* [HGM,HR] make sure board size is acceptable */
863 if(appData.NrFiles > BOARD_FILES ||
864 appData.NrRanks > BOARD_RANKS )
865 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
868 /* This feature does not work; animation needs a rewrite */
869 appData.highlightDragging = FALSE;
873 gameInfo.variant = StringToVariant(appData.variant);
877 * determine size, based on supplied or remembered -size, or screen size
879 if (isdigit(appData.boardSize[0])) {
880 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
881 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
882 &fontPxlSize, &smallLayout, &tinyLayout);
884 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
885 programName, appData.boardSize);
889 /* Find some defaults; use the nearest known size */
890 SizeDefaults *szd, *nearest;
891 int distance = 99999;
892 nearest = szd = sizeDefaults;
893 while (szd->name != NULL) {
894 if (abs(szd->squareSize - squareSize) < distance) {
896 distance = abs(szd->squareSize - squareSize);
897 if (distance == 0) break;
901 if (i < 2) lineGap = nearest->lineGap;
902 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
903 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
904 if (i < 5) fontPxlSize = nearest->fontPxlSize;
905 if (i < 6) smallLayout = nearest->smallLayout;
906 if (i < 7) tinyLayout = nearest->tinyLayout;
909 SizeDefaults *szd = sizeDefaults;
910 if (*appData.boardSize == NULLCHAR) {
911 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
912 guint screenwidth = gdk_screen_get_width(screen);
913 guint screenheight = gdk_screen_get_height(screen);
914 while (screenwidth < szd->minScreenSize ||
915 screenheight < szd->minScreenSize) {
918 if (szd->name == NULL) szd--;
919 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
921 while (szd->name != NULL &&
922 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
923 if (szd->name == NULL) {
924 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
925 programName, appData.boardSize);
929 squareSize = szd->squareSize;
930 lineGap = szd->lineGap;
931 clockFontPxlSize = szd->clockFontPxlSize;
932 coordFontPxlSize = szd->coordFontPxlSize;
933 fontPxlSize = szd->fontPxlSize;
934 smallLayout = szd->smallLayout;
935 tinyLayout = szd->tinyLayout;
936 // [HGM] font: use defaults from settings file if available and not overruled
939 defaultLineGap = lineGap;
940 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
942 /* [HR] height treated separately (hacked) */
943 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
944 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
947 * Determine what fonts to use.
950 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
954 * Detect if there are not enough colors available and adapt.
957 if (DefaultDepth(xDisplay, xScreen) <= 2) {
958 appData.monoMode = True;
962 forceMono = MakeColors();
965 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
967 appData.monoMode = True;
970 ParseIcsTextColors();
976 layoutName = "tinyLayout";
977 } else if (smallLayout) {
978 layoutName = "smallLayout";
980 layoutName = "normalLayout";
983 wpMain.width = -1; // prevent popup sizes window
984 optList = BoardPopUp(squareSize, lineGap, (void*)
994 InitDrawingHandle(optList + W_BOARD);
995 shellWidget = shells[BoardWindow];
996 currBoard = &optList[W_BOARD];
997 boardWidget = optList[W_BOARD].handle;
998 menuBarWidget = optList[W_MENU].handle;
999 dropMenu = optList[W_DROP].handle;
1000 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1002 formWidget = XtParent(boardWidget);
1003 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1004 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1005 XtGetValues(optList[W_WHITE].handle, args, 2);
1006 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1007 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1008 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1009 XtGetValues(optList[W_PAUSE].handle, args, 2);
1013 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1014 // not need to go into InitDrawingSizes().
1018 // add accelerators to main shell
1019 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1022 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1024 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
1025 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
1026 mainwindowIcon = WhiteIcon;
1027 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1031 * Create a cursor for the board widget.
1034 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1035 XChangeWindowAttributes(xDisplay, xBoardWindow,
1036 CWCursor, &window_attributes);
1040 * Inhibit shell resizing.
1043 shellArgs[0].value = (XtArgVal) &w;
1044 shellArgs[1].value = (XtArgVal) &h;
1045 XtGetValues(shellWidget, shellArgs, 2);
1046 shellArgs[4].value = shellArgs[2].value = w;
1047 shellArgs[5].value = shellArgs[3].value = h;
1048 // XtSetValues(shellWidget, &shellArgs[2], 4);
1051 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1052 // It wil only become known asynchronously, when we first write a string into it.
1053 // This will then change the clock widget height, which triggers resizing the top-level window
1054 // and a configure event. Only then can we know the total height of the top-level window,
1055 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1056 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1059 gtk_widget_get_allocation(shells[BoardWindow], &a);
1060 w = a.width; h = a.height;
1061 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1062 clockKludge = hc = a.height;
1063 gtk_widget_get_allocation(boardWidget, &a);
1064 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1065 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1071 if(appData.logoSize)
1072 { // locate and read user logo
1074 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1075 ASSIGN(userLogo, buf);
1078 if (appData.animate || appData.animateDragging)
1081 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1082 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1084 /* [AS] Restore layout */
1085 if( wpMoveHistory.visible ) {
1089 if( wpEvalGraph.visible )
1094 if( wpEngineOutput.visible ) {
1095 EngineOutputPopUp();
1100 if (errorExitStatus == -1) {
1101 if (appData.icsActive) {
1102 /* We now wait until we see "login:" from the ICS before
1103 sending the logon script (problems with timestamp otherwise) */
1104 /*ICSInitScript();*/
1105 if (appData.icsInputBox) ICSInputBoxPopUp();
1109 signal(SIGWINCH, TermSizeSigHandler);
1111 signal(SIGINT, IntSigHandler);
1112 signal(SIGTERM, IntSigHandler);
1113 if (*appData.cmailGameName != NULLCHAR) {
1114 signal(SIGUSR1, CmailSigHandler);
1118 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1121 // XtSetKeyboardFocus(shellWidget, formWidget);
1123 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1126 /* check for GTK events and process them */
1129 gtk_main_iteration();
1132 if (appData.debugMode) fclose(debugFP); // [DM] debug
1137 TermSizeSigHandler (int sig)
1143 IntSigHandler (int sig)
1149 CmailSigHandler (int sig)
1154 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1156 /* Activate call-back function CmailSigHandlerCallBack() */
1157 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1159 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1163 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1166 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1168 /**** end signal code ****/
1171 #define Abs(n) ((n)<0 ? -(n) : (n))
1175 InsertPxlSize (char *pattern, int targetPxlSize)
1177 char *base_fnt_lst, strInt[12], *p, *q;
1178 int alternatives, i, len, strIntLen;
1181 * Replace the "*" (if present) in the pixel-size slot of each
1182 * alternative with the targetPxlSize.
1186 while ((p = strchr(p, ',')) != NULL) {
1190 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1191 strIntLen = strlen(strInt);
1192 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1196 while (alternatives--) {
1197 char *comma = strchr(p, ',');
1198 for (i=0; i<14; i++) {
1199 char *hyphen = strchr(p, '-');
1201 if (comma && hyphen > comma) break;
1202 len = hyphen + 1 - p;
1203 if (i == 7 && *p == '*' && len == 2) {
1205 memcpy(q, strInt, strIntLen);
1215 len = comma + 1 - p;
1222 return base_fnt_lst;
1227 CreateFontSet (char *base_fnt_lst)
1230 char **missing_list;
1234 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1235 &missing_list, &missing_count, &def_string);
1236 if (appData.debugMode) {
1238 XFontStruct **font_struct_list;
1239 char **font_name_list;
1240 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1242 fprintf(debugFP, " got list %s, locale %s\n",
1243 XBaseFontNameListOfFontSet(fntSet),
1244 XLocaleOfFontSet(fntSet));
1245 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1246 for (i = 0; i < count; i++) {
1247 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1250 for (i = 0; i < missing_count; i++) {
1251 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1254 if (fntSet == NULL) {
1255 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1261 #else // not ENABLE_NLS
1263 * Find a font that matches "pattern" that is as close as
1264 * possible to the targetPxlSize. Prefer fonts that are k
1265 * pixels smaller to fonts that are k pixels larger. The
1266 * pattern must be in the X Consortium standard format,
1267 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1268 * The return value should be freed with XtFree when no
1272 FindFont (char *pattern, int targetPxlSize)
1274 char **fonts, *p, *best, *scalable, *scalableTail;
1275 int i, j, nfonts, minerr, err, pxlSize;
1278 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1280 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1281 programName, pattern);
1288 for (i=0; i<nfonts; i++) {
1291 if (*p != '-') continue;
1293 if (*p == NULLCHAR) break;
1294 if (*p++ == '-') j++;
1296 if (j < 7) continue;
1299 scalable = fonts[i];
1302 err = pxlSize - targetPxlSize;
1303 if (Abs(err) < Abs(minerr) ||
1304 (minerr > 0 && err < 0 && -err == minerr)) {
1310 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1311 /* If the error is too big and there is a scalable font,
1312 use the scalable font. */
1313 int headlen = scalableTail - scalable;
1314 p = (char *) XtMalloc(strlen(scalable) + 10);
1315 while (isdigit(*scalableTail)) scalableTail++;
1316 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1318 p = (char *) XtMalloc(strlen(best) + 2);
1319 safeStrCpy(p, best, strlen(best)+1 );
1321 if (appData.debugMode) {
1322 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1323 pattern, targetPxlSize, p);
1325 XFreeFontNames(fonts);
1332 EnableNamedMenuItem (char *menuRef, int state)
1334 MenuItem *item = MenuNameToItem(menuRef);
1336 if(item) gtk_widget_set_sensitive(item->handle, state);
1340 EnableButtonBar (int state)
1343 XtSetSensitive(optList[W_BUTTON].handle, state);
1349 SetMenuEnables (Enables *enab)
1351 while (enab->name != NULL) {
1352 EnableNamedMenuItem(enab->name, enab->value);
1357 gboolean KeyPressProc(window, eventkey, data)
1359 GdkEventKey *eventkey;
1363 MoveTypeInProc(eventkey); // pop up for typed in moves
1366 /* check for other key values */
1367 switch(eventkey->keyval) {
1379 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1380 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1382 if(*nprms == 0) return;
1383 item = MenuNameToItem(prms[0]);
1384 if(item) ((MenuProc *) item->proc) ();
1398 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1399 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1400 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1401 dmEnables[i].piece);
1402 XtSetSensitive(entry, p != NULL || !appData.testLegality
1403 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1404 && !appData.icsActive));
1406 while (p && *p++ == dmEnables[i].piece) count++;
1407 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1409 XtSetArg(args[j], XtNlabel, label); j++;
1410 XtSetValues(entry, args, j);
1416 do_flash_delay (unsigned long msec)
1422 FlashDelay (int flash_delay)
1424 if(flash_delay) do_flash_delay(flash_delay);
1428 Fraction (int x, int start, int stop)
1430 double f = ((double) x - start)/(stop - start);
1431 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1435 static WindowPlacement wpNew;
1438 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1440 int touch=0, fudge = 2, f = 2;
1441 GetActualPlacement(sh, wp);
1442 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1443 if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x) < fudge) touch = 2; else // left touch
1444 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1445 if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y) < fudge) touch = 4; // top touch
1446 //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);
1447 if(!touch ) return; // only windows that touch co-move
1448 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1449 int heightInc = wpNew.height - wpMain.height;
1450 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1451 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1452 wp->y += fracTop * heightInc;
1453 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1455 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1457 wp->height += heightInc;
1458 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1459 int widthInc = wpNew.width - wpMain.width;
1460 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1461 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1462 wp->y += fracLeft * widthInc;
1463 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1465 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1467 wp->width += widthInc;
1469 wp->x += wpNew.x - wpMain.x;
1470 wp->y += wpNew.y - wpMain.y;
1471 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1472 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1474 XtSetArg(args[j], XtNx, wp->x); j++;
1475 XtSetArg(args[j], XtNy, wp->y); j++;
1476 XtSetValues(sh, args, j);
1478 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1479 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1480 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1484 ReSize (WindowPlacement *wp)
1487 int sqx, sqy, w, h, hc, lg = lineGap;
1488 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1489 hc = a.height; // clock height can depend on single / double line clock text!
1490 if(clockKludge == a.height) return; // wait for clock to get final size at startup
1491 if(clockKludge) { // clock height OK now; calculate desired initial board height
1493 wp->height = BOARD_HEIGHT * (squareSize + lineGap) + lineGap + marginH + hc;
1495 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1496 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1497 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1498 if(sqy < sqx) sqx = sqy;
1499 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1500 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1501 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1502 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1503 if(sqy < sqx) sqx = sqy;
1505 if(sqx != squareSize) {
1506 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1507 squareSize = sqx; // adopt new square size
1508 CreatePNGPieces(); // make newly scaled pieces
1509 InitDrawingSizes(0, 0); // creates grid etc.
1510 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1511 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1512 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1513 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1514 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1517 static guint delayedDragTag = 0;
1526 // GetActualPlacement(shellWidget, &wpNew);
1527 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1528 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1529 busy = 0; return; // false alarm
1532 if(appData.useStickyWindows) {
1533 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1534 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1535 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1536 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1539 DrawPosition(True, NULL);
1540 if(delayedDragTag) g_source_remove(delayedDragTag);
1541 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1548 //printf("old timr = %d\n", delayedDragTag);
1549 if(delayedDragTag) g_source_remove(delayedDragTag);
1550 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1551 //printf("new timr = %d\n", delayedDragTag);
1555 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1557 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1559 wpNew.x = event->configure.x;
1560 wpNew.y = event->configure.y;
1561 wpNew.width = event->configure.width;
1562 wpNew.height = event->configure.height;
1563 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1569 /* Disable all user input other than deleting the window */
1570 static int frozen = 0;
1576 /* Grab by a widget that doesn't accept input */
1577 gtk_grab_add(optList[W_MESSG].handle);
1581 /* Undo a FreezeUI */
1585 if (!frozen) return;
1586 gtk_grab_remove(optList[W_MESSG].handle);
1593 static int oldPausing = FALSE;
1594 static GameMode oldmode = (GameMode) -1;
1596 if (!boardWidget) return;
1598 if (pausing != oldPausing) {
1599 oldPausing = pausing;
1600 MarkMenuItem("Mode.Pause", pausing);
1602 if (appData.showButtonBar) {
1603 /* Always toggle, don't set. Previous code messes up when
1604 invoked while the button is pressed, as releasing it
1605 toggles the state again. */
1607 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1608 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1612 wname = ModeToWidgetName(oldmode);
1613 if (wname != NULL) {
1614 MarkMenuItem(wname, False);
1616 wname = ModeToWidgetName(gameMode);
1617 if (wname != NULL) {
1618 MarkMenuItem(wname, True);
1621 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1623 /* Maybe all the enables should be handled here, not just this one */
1624 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1626 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1631 * Button/menu procedures
1634 void CopyFileToClipboard(gchar *filename)
1636 gchar *selection_tmp;
1640 FILE* f = fopen(filename, "r");
1643 if (f == NULL) return;
1647 selection_tmp = g_try_malloc(len + 1);
1648 if (selection_tmp == NULL) {
1649 printf("Malloc failed in CopyFileToClipboard\n");
1652 count = fread(selection_tmp, 1, len, f);
1655 g_free(selection_tmp);
1658 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1660 // copy selection_tmp to clipboard
1661 GdkDisplay *gdisp = gdk_display_get_default();
1663 g_free(selection_tmp);
1666 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1667 gtk_clipboard_set_text(cb, selection_tmp, -1);
1668 g_free(selection_tmp);
1672 CopySomething (char *src)
1674 GdkDisplay *gdisp = gdk_display_get_default();
1676 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1677 if (gdisp == NULL) return;
1678 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1679 gtk_clipboard_set_text(cb, src, -1);
1683 PastePositionProc ()
1685 GdkDisplay *gdisp = gdk_display_get_default();
1689 if (gdisp == NULL) return;
1690 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1691 fenstr = gtk_clipboard_wait_for_text(cb);
1692 if (fenstr==NULL) return; // nothing had been selected to copy
1693 EditPositionPasteFEN(fenstr);
1705 // get game from clipboard
1706 GdkDisplay *gdisp = gdk_display_get_default();
1707 if (gdisp == NULL) return;
1708 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1709 text = gtk_clipboard_wait_for_text(cb);
1710 if (text == NULL) return; // nothing to paste
1713 // write to temp file
1714 if (text == NULL || len == 0) {
1715 return; //nothing to paste
1717 f = fopen(gamePasteFilename, "w");
1719 DisplayError(_("Can't open temp file"), errno);
1722 fwrite(text, 1, len, f);
1726 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1733 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1739 void MoveTypeInProc(eventkey)
1740 GdkEventKey *eventkey;
1744 // ingnore if ctrl, alt, or meta is pressed
1745 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1749 buf[0]=eventkey->keyval;
1751 if (eventkey->keyval > 32 && eventkey->keyval < 256)
1757 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1759 if (!TempBackwardActive) {
1760 TempBackwardActive = True;
1766 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1768 /* Check to see if triggered by a key release event for a repeating key.
1769 * If so the next queued event will be a key press of the same key at the same time */
1770 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1772 XPeekEvent(xDisplay, &next);
1773 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1774 next.xkey.keycode == event->xkey.keycode)
1778 TempBackwardActive = False;
1784 { // called from menu
1786 system("%s ./man.command", appData.sysOpen);
1788 system("xterm -e man xboard &");
1793 SetWindowTitle (char *text, char *title, char *icon)
1798 if (appData.titleInWindow) {
1800 XtSetArg(args[i], XtNlabel, text); i++;
1801 XtSetValues(titleWidget, args, i);
1804 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1805 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1806 XtSetValues(shellWidget, args, i);
1807 XSync(xDisplay, False);
1809 if (appData.titleInWindow) {
1810 SetWidgetLabel(titleWidget, text);
1812 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1817 DisplayIcsInteractionTitle (String message)
1820 if (oldICSInteractionTitle == NULL) {
1821 /* Magic to find the old window title, adapted from vim */
1822 char *wina = getenv("WINDOWID");
1824 Window win = (Window) atoi(wina);
1825 Window root, parent, *children;
1826 unsigned int nchildren;
1827 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1829 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1830 if (!XQueryTree(xDisplay, win, &root, &parent,
1831 &children, &nchildren)) break;
1832 if (children) XFree((void *)children);
1833 if (parent == root || parent == 0) break;
1836 XSetErrorHandler(oldHandler);
1838 if (oldICSInteractionTitle == NULL) {
1839 oldICSInteractionTitle = "xterm";
1842 printf("\033]0;%s\007", message);
1849 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1851 GtkWidget *w = (GtkWidget *) opt->handle;
1858 strcpy(bgcolor, "black");
1859 strcpy(fgcolor, "white");
1861 strcpy(bgcolor, "white");
1862 strcpy(fgcolor, "black");
1865 appData.lowTimeWarning &&
1866 (timer / 1000) < appData.icsAlarmTime) {
1867 strcpy(fgcolor, appData.lowTimeWarningColor);
1870 gdk_color_parse( bgcolor, &col );
1871 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1873 if (appData.clockMode) {
1874 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1875 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1877 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
1878 bgcolor, fgcolor, color);
1880 gtk_label_set_markup(GTK_LABEL(w), markup);
1884 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1887 SetClockIcon (int color)
1889 GdkPixbuf *pm = *clockIcons[color];
1890 if (mainwindowIcon != pm) {
1891 mainwindowIcon = pm;
1892 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1896 #define INPUT_SOURCE_BUF_SIZE 8192
1905 char buf[INPUT_SOURCE_BUF_SIZE];
1910 DoInputCallback(io, cond, data)
1915 /* read input from one of the input source (for example a chess program, ICS, etc).
1916 * and call a function that will handle the input
1923 /* All information (callback function, file descriptor, etc) is
1924 * saved in an InputSource structure
1926 InputSource *is = (InputSource *) data;
1928 if (is->lineByLine) {
1929 count = read(is->fd, is->unused,
1930 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1932 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1935 is->unused += count;
1937 /* break input into lines and call the callback function on each
1940 while (p < is->unused) {
1941 q = memchr(p, '\n', is->unused - p);
1942 if (q == NULL) break;
1944 (is->func)(is, is->closure, p, q - p, 0);
1947 /* remember not yet used part of the buffer */
1949 while (p < is->unused) {
1954 /* read maximum length of input buffer and send the whole buffer
1955 * to the callback function
1957 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1962 (is->func)(is, is->closure, is->buf, count, error);
1964 return True; // Must return true or the watch will be removed
1967 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1974 GIOChannel *channel;
1975 ChildProc *cp = (ChildProc *) pr;
1977 is = (InputSource *) calloc(1, sizeof(InputSource));
1978 is->lineByLine = lineByLine;
1982 is->fd = fileno(stdin);
1984 is->kind = cp->kind;
1985 is->fd = cp->fdFrom;
1988 is->unused = is->buf;
1992 /* GTK-TODO: will this work on windows?*/
1994 channel = g_io_channel_unix_new(is->fd);
1995 g_io_channel_set_close_on_unref (channel, TRUE);
1996 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1998 is->closure = closure;
1999 return (InputSourceRef) is;
2004 RemoveInputSource(isr)
2007 InputSource *is = (InputSource *) isr;
2009 if (is->sid == 0) return;
2010 g_source_remove(is->sid);
2017 static Boolean frameWaiting;
2020 FrameAlarm (int sig)
2022 frameWaiting = False;
2023 /* In case System-V style signals. Needed?? */
2024 signal(SIGALRM, FrameAlarm);
2028 FrameDelay (int time)
2030 struct itimerval delay;
2033 frameWaiting = True;
2034 signal(SIGALRM, FrameAlarm);
2035 delay.it_interval.tv_sec =
2036 delay.it_value.tv_sec = time / 1000;
2037 delay.it_interval.tv_usec =
2038 delay.it_value.tv_usec = (time % 1000) * 1000;
2039 setitimer(ITIMER_REAL, &delay, NULL);
2040 while (frameWaiting) pause();
2041 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2042 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2043 setitimer(ITIMER_REAL, &delay, NULL);
2050 FrameDelay (int time)
2053 XSync(xDisplay, False);
2055 // gtk_main_iteration_do(False);
2058 usleep(time * 1000);
2064 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2066 char buf[MSG_SIZ], *logoName = buf;
2067 if(appData.logo[n][0]) {
2068 logoName = appData.logo[n];
2069 } else if(appData.autoLogo) {
2070 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2071 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2072 } else if(appData.directory[n] && appData.directory[n][0]) {
2073 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2077 { ASSIGN(cps->programLogo, logoName); }
2081 UpdateLogos (int displ)
2083 if(optList[W_WHITE-1].handle == NULL) return;
2084 LoadLogo(&first, 0, 0);
2085 LoadLogo(&second, 1, appData.icsActive);
2086 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2090 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2101 GtkFileFilter *gtkfilter;
2102 GtkFileFilter *gtkfilter_all;
2104 char fileext[10] = "";
2105 char *result = NULL;
2108 /* make a copy of the filter string, so that strtok can work with it*/
2109 cp = strdup(filter);
2111 /* add filters for file extensions */
2112 gtkfilter = gtk_file_filter_new();
2113 gtkfilter_all = gtk_file_filter_new();
2115 /* one filter to show everything */
2116 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2117 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2119 /* add filter if present */
2120 result = strtok(cp, space);
2121 while( result != NULL ) {
2122 snprintf(fileext,10,"*%s",result);
2123 result = strtok( NULL, space );
2124 gtk_file_filter_add_pattern(gtkfilter, fileext);
2127 /* second filter to only show what's useful */
2128 gtk_file_filter_set_name (gtkfilter,filter);
2130 if (openMode[0] == 'r')
2132 dialog = gtk_file_chooser_dialog_new (label,
2134 GTK_FILE_CHOOSER_ACTION_OPEN,
2135 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2136 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2141 dialog = gtk_file_chooser_dialog_new (label,
2143 GTK_FILE_CHOOSER_ACTION_SAVE,
2144 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2145 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2147 /* add filename suggestions */
2148 if (strlen(def) > 0 )
2149 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2151 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2155 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2156 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2157 /* activate filter */
2158 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2160 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2165 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2168 f = fopen(filename, openMode);
2171 DisplayError(_("Failed to open file"), errno);
2175 /* TODO add indec */
2177 ASSIGN(*name, filename);
2178 ScheduleDelayedEvent(DelayedLoad, 50);
2183 gtk_widget_destroy (dialog);