2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
64 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.h>
69 # if HAVE_SYS_SOCKET_H
70 # include <sys/socket.h>
71 # include <netinet/in.h>
73 # else /* not HAVE_SYS_SOCKET_H */
74 # if HAVE_LAN_SOCKET_H
75 # include <lan/socket.h>
77 # include <lan/netdb.h>
78 # else /* not HAVE_LAN_SOCKET_H */
79 # define OMIT_SOCKETS 1
80 # endif /* not HAVE_LAN_SOCKET_H */
81 # endif /* not HAVE_SYS_SOCKET_H */
82 #endif /* !OMIT_SOCKETS */
87 #else /* not STDC_HEADERS */
88 extern char *getenv();
91 # else /* not HAVE_STRING_H */
93 # endif /* not HAVE_STRING_H */
94 #endif /* not STDC_HEADERS */
97 # include <sys/fcntl.h>
98 #else /* not HAVE_SYS_FCNTL_H */
101 # endif /* HAVE_FCNTL_H */
102 #endif /* not HAVE_SYS_FCNTL_H */
104 #if HAVE_SYS_SYSTEMINFO_H
105 # include <sys/systeminfo.h>
106 #endif /* HAVE_SYS_SYSTEMINFO_H */
108 #if TIME_WITH_SYS_TIME
109 # include <sys/time.h>
113 # include <sys/time.h>
124 # include <sys/wait.h>
129 # define NAMLEN(dirent) strlen((dirent)->d_name)
130 # define HAVE_DIR_STRUCT
132 # define dirent direct
133 # define NAMLEN(dirent) (dirent)->d_namlen
135 # include <sys/ndir.h>
136 # define HAVE_DIR_STRUCT
139 # include <sys/dir.h>
140 # define HAVE_DIR_STRUCT
144 # define HAVE_DIR_STRUCT
152 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
155 #include "frontend.h"
157 #include "backendz.h"
165 #include "engineoutput.h"
171 # include <gtkmacintegration/gtkosxapplication.h>
172 // prevent pathname of positional file argument provided by OS X being be mistaken for option name
173 // (price is that we won't recognize Windows option format anymore).
175 // redefine some defaults
178 # undef SETTINGS_FILE
179 # define ICS_LOGON "Library/Preferences/XboardICS.conf"
180 # define DATADIR dataDir
181 # define SETTINGS_FILE masterSettings
182 char dataDir[MSG_SIZ]; // for expanding ~~
183 char masterSettings[MSG_SIZ];
192 #define usleep(t) _sleep2(((t)+500)/1000)
196 # define _(s) gettext (s)
197 # define N_(s) gettext_noop (s)
203 int main P((int argc, char **argv));
204 RETSIGTYPE CmailSigHandler P((int sig));
205 RETSIGTYPE IntSigHandler P((int sig));
206 RETSIGTYPE TermSizeSigHandler P((int sig));
208 char *InsertPxlSize P((char *pattern, int targetPxlSize));
209 XFontSet CreateFontSet P((char *base_fnt_lst));
211 char *FindFont P((char *pattern, int targetPxlSize));
213 void DelayedDrag P((void));
214 void ICSInputBoxPopUp P((void));
215 void MoveTypeInProc P((GdkEventKey *eventkey));
216 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
217 Boolean TempBackwardActive = False;
218 void DisplayMove P((int moveNumber));
219 void update_ics_width P(());
220 int CopyMemoProc P(());
221 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
225 XFontSet fontSet, clockFontSet;
228 XFontStruct *clockFontStruct;
230 Font coordFontID, countFontID;
231 XFontStruct *coordFontStruct, *countFontStruct;
233 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
234 GtkWidget *mainwindow;
236 Option *optList; // contains all widgets of main window
239 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
242 static GdkPixbuf *mainwindowIcon=NULL;
243 static GdkPixbuf *WhiteIcon=NULL;
244 static GdkPixbuf *BlackIcon=NULL;
246 /* key board accelerators */
247 GtkAccelGroup *GtkAccelerators;
249 typedef unsigned int BoardSize;
251 Boolean chessProgram;
253 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
254 int smallLayout = 0, tinyLayout = 0,
255 marginW, marginH, // [HGM] for run-time resizing
256 fromX = -1, fromY = -1, toX, toY, commentUp = False,
257 errorExitStatus = -1, defaultLineGap;
259 Dimension textHeight;
261 char *chessDir, *programName, *programVersion;
262 Boolean alwaysOnTop = False;
263 char *icsTextMenuString;
265 char *firstChessProgramNames;
266 char *secondChessProgramNames;
268 WindowPlacement wpMain;
269 WindowPlacement wpConsole;
270 WindowPlacement wpComment;
271 WindowPlacement wpMoveHistory;
272 WindowPlacement wpEvalGraph;
273 WindowPlacement wpEngineOutput;
274 WindowPlacement wpGameList;
275 WindowPlacement wpTags;
276 WindowPlacement wpDualBoard;
278 /* This magic number is the number of intermediate frames used
279 in each half of the animation. For short moves it's reduced
280 by 1. The total number of frames will be factor * 2 + 1. */
283 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
290 DropMenuEnables dmEnables[] = {
299 XtResource clientResources[] = {
300 { "flashCount", "flashCount", XtRInt, sizeof(int),
301 XtOffset(AppDataPtr, flashCount), XtRImmediate,
302 (XtPointer) FLASH_COUNT },
306 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
307 char globalTranslations[] =
308 ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
309 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
310 :<KeyDown>Return: TempBackwardProc() \n \
311 :<KeyUp>Return: TempForwardProc() \n";
313 char ICSInputTranslations[] =
314 "<Key>Up: UpKeyProc() \n "
315 "<Key>Down: DownKeyProc() \n "
316 "<Key>Return: EnterKeyProc() \n";
318 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
319 // as the widget is destroyed before the up-click can call extend-end
320 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
323 String xboardResources[] = {
324 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
332 gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
335 //---------------------------------------------------------------------------------------------------------
336 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
339 #define CW_USEDEFAULT (1<<31)
340 #define ICS_TEXT_MENU_SIZE 90
341 #define DEBUG_FILE "xboard.debug"
342 #define SetCurrentDirectory chdir
343 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
347 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
350 // front-end part of option handling
352 // [HGM] This platform-dependent table provides the location for storing the color info
353 extern char *crWhite, * crBlack;
357 &appData.whitePieceColor,
358 &appData.blackPieceColor,
359 &appData.lightSquareColor,
360 &appData.darkSquareColor,
361 &appData.highlightSquareColor,
362 &appData.premoveHighlightColor,
363 &appData.lowTimeWarningColor,
374 // [HGM] font: keep a font for each square size, even non-stndard ones
377 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
378 char *fontTable[NUM_FONTS][MAX_SIZE];
381 ParseFont (char *name, int number)
382 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
384 if(sscanf(name, "size%d:", &size)) {
385 // [HGM] font: font is meant for specific boardSize (likely from settings file);
386 // defer processing it until we know if it matches our board size
387 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
388 fontTable[number][size] = strdup(strchr(name, ':')+1);
389 fontValid[number][size] = True;
394 case 0: // CLOCK_FONT
395 appData.clockFont = strdup(name);
397 case 1: // MESSAGE_FONT
398 appData.font = strdup(name);
400 case 2: // COORD_FONT
401 appData.coordFont = strdup(name);
406 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
411 { // only 2 fonts currently
412 appData.clockFont = CLOCK_FONT_NAME;
413 appData.coordFont = COORD_FONT_NAME;
414 appData.font = DEFAULT_FONT_NAME;
419 { // no-op, until we identify the code for this already in XBoard and move it here
423 ParseColor (int n, char *name)
424 { // in XBoard, just copy the color-name string
425 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
429 ParseTextAttribs (ColorClass cc, char *s)
431 (&appData.colorShout)[cc] = strdup(s);
435 ParseBoardSize (void *addr, char *name)
437 appData.boardSize = strdup(name);
442 { // In XBoard the sound-playing program takes care of obtaining the actual sound
446 SetCommPortDefaults ()
447 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
450 // [HGM] args: these three cases taken out to stay in front-end
452 SaveFontArg (FILE *f, ArgDescriptor *ad)
455 int i, n = (int)(intptr_t)ad->argLoc;
457 case 0: // CLOCK_FONT
458 name = appData.clockFont;
460 case 1: // MESSAGE_FONT
463 case 2: // COORD_FONT
464 name = appData.coordFont;
469 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
470 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
471 fontTable[n][squareSize] = strdup(name);
472 fontValid[n][squareSize] = True;
475 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
476 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
481 { // nothing to do, as the sounds are at all times represented by their text-string names already
485 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
486 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
487 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
491 SaveColor (FILE *f, ArgDescriptor *ad)
492 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
493 if(colorVariable[(int)(intptr_t)ad->argLoc])
494 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
498 SaveBoardSize (FILE *f, char *name, void *addr)
499 { // wrapper to shield back-end from BoardSize & sizeInfo
500 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
504 ParseCommPortSettings (char *s)
505 { // no such option in XBoard (yet)
511 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
515 gtk_widget_get_allocation(shell, &a);
516 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
520 wp->height = a.height;
521 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
522 frameX = 3; frameY = 3; // remember to decide if windows touch
527 { // wrapper to shield use of window handles from back-end (make addressible by number?)
528 // In XBoard this will have to wait until awareness of window parameters is implemented
529 GetActualPlacement(shellWidget, &wpMain);
530 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
531 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
532 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
533 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
534 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
535 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
539 PrintCommPortSettings (FILE *f, char *name)
540 { // This option does not exist in XBoard
544 EnsureOnScreen (int *x, int *y, int minX, int minY)
551 { // [HGM] args: allows testing if main window is realized from back-end
552 return DialogExists(BoardWindow);
556 PopUpStartupDialog ()
557 { // start menu not implemented in XBoard
561 ConvertToLine (int argc, char **argv)
563 static char line[128*1024], buf[1024];
567 for(i=1; i<argc; i++)
569 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
570 && argv[i][0] != '{' )
571 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
573 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
574 strncat(line, buf, 128*1024 - strlen(line) - 1 );
577 line[strlen(line)-1] = NULLCHAR;
581 //--------------------------------------------------------------------------------------------
586 ResizeBoardWindow (int w, int h, int inhibit)
589 if(clockKludge) return; // ignore as long as clock does not have final height
590 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
591 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
592 h += marginH + a.height + 1;
593 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
598 { // dummy, as the GTK code does not make colors in advance
603 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
604 { // determine what fonts to use, and create them
609 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
610 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
611 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
612 appData.font = fontTable[MESSAGE_FONT][squareSize];
613 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
614 appData.coordFont = fontTable[COORD_FONT][squareSize];
617 appData.font = InsertPxlSize(appData.font, fontPxlSize);
618 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
619 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
620 fontSet = CreateFontSet(appData.font);
621 clockFontSet = CreateFontSet(appData.clockFont);
623 /* For the coordFont, use the 0th font of the fontset. */
624 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
625 XFontStruct **font_struct_list;
626 XFontSetExtents *fontSize;
627 char **font_name_list;
628 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
629 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
630 coordFontStruct = XQueryFont(xDisplay, coordFontID);
631 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
632 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
635 appData.font = FindFont(appData.font, fontPxlSize);
636 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
637 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
638 clockFontID = XLoadFont(xDisplay, appData.clockFont);
639 clockFontStruct = XQueryFont(xDisplay, clockFontID);
640 coordFontID = XLoadFont(xDisplay, appData.coordFont);
641 coordFontStruct = XQueryFont(xDisplay, coordFontID);
642 // textHeight in !NLS mode!
644 countFontID = coordFontID; // [HGM] holdings
645 countFontStruct = coordFontStruct;
647 xdb = XtDatabase(xDisplay);
649 XrmPutLineResource(&xdb, "*international: True");
650 vTo.size = sizeof(XFontSet);
651 vTo.addr = (XtPointer) &fontSet;
652 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
654 XrmPutStringResource(&xdb, "*font", appData.font);
665 case ArgInt: p = " N"; break;
666 case ArgString: p = " STR"; break;
667 case ArgBoolean: p = " TF"; break;
668 case ArgSettingsFilename:
669 case ArgBackupSettingsFile:
670 case ArgFilename: p = " FILE"; break;
671 case ArgX: p = " Nx"; break;
672 case ArgY: p = " Ny"; break;
673 case ArgAttribs: p = " TEXTCOL"; break;
674 case ArgColor: p = " COL"; break;
675 case ArgFont: p = " FONT"; break;
676 case ArgBoardSize: p = " SIZE"; break;
677 case ArgFloat: p = " FLOAT"; break;
682 case ArgCommSettings:
694 ArgDescriptor *q, *p = argDescriptors+5;
695 printf("\nXBoard accepts the following options:\n"
696 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
697 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
698 " SIZE = board-size spec(s)\n"
699 " Within parentheses are short forms, or options to set to true or false.\n"
700 " Persistent options (saved in the settings file) are marked with *)\n\n");
702 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
703 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
704 if(p->save) strcat(buf+len, "*");
705 for(q=p+1; q->argLoc == p->argLoc; q++) {
706 if(q->argName[0] == '-') continue;
707 strcat(buf+len, q == p+1 ? " (" : " ");
708 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
710 if(q != p+1) strcat(buf+len, ")");
712 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
715 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
719 SlaveResize (Option *opt)
721 static int slaveW, slaveH, w, h;
724 gtk_widget_get_allocation(shells[DummyDlg], &a);
725 w = a.width; h = a.height;
726 gtk_widget_get_allocation(opt->handle, &a);
727 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
728 slaveH = h - a.height + 13;
730 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
734 static char clickedFile[MSG_SIZ];
738 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
739 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
740 if(suppress) { // we just started XBoard without arguments
741 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
742 } else { // we are running something presumably useful
744 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
745 system(buf); // start new instance on this file
752 main (int argc, char **argv)
754 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
755 int boardWidth, boardHeight, w, h;
757 int forceMono = False;
759 srandom(time(0)); // [HGM] book: make random truly random
761 setbuf(stdout, NULL);
762 setbuf(stderr, NULL);
765 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
766 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
770 if(argc > 1 && !strcmp(argv[1], "--help" )) {
776 gtk_init (&argc, &argv);
778 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
779 GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
780 char *path = gtkosx_application_get_bundle_path();
781 strncpy(dataDir, path, MSG_SIZ);
782 snprintf(masterSettings, MSG_SIZ, "%s/../Resources/etc/xboard.conf", path);
783 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
784 // we must call application ready before we can get the signal,
785 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
786 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
787 gtkosx_application_ready(theApp);
788 suppress = (argc == 1 || argc > 1 && argv[1][00] != '-'); // OSX sends signal even if name was already argv[1]!
789 if(argc == 1) { // called without args: OSX open-file signal might follow
790 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
791 usleep(10000); // wait 10 msec (and hope this is long enough).
792 while(gtk_events_pending())
793 gtk_main_iteration(); // process all events that came in upto now
794 suppress = 0; // future open-file signals should start new instance
795 if(clickedFile[0]) { // we were sent an open-file signal with filename!
796 fakeArgv[0] = argv[0];
797 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
803 /* set up keyboard accelerators group */
804 GtkAccelerators = gtk_accel_group_new();
806 programName = strrchr(argv[0], '/');
807 if (programName == NULL)
808 programName = argv[0];
813 // if (appData.debugMode) {
814 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
817 bindtextdomain(PACKAGE, LOCALEDIR);
818 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
822 appData.boardSize = "";
823 InitAppData(ConvertToLine(argc, argv));
825 if (p == NULL) p = "/tmp";
826 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
827 gameCopyFilename = (char*) malloc(i);
828 gamePasteFilename = (char*) malloc(i);
829 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
830 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
832 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
833 static char buf[MSG_SIZ];
834 EscapeExpand(buf, appData.firstInitString);
835 appData.firstInitString = strdup(buf);
836 EscapeExpand(buf, appData.secondInitString);
837 appData.secondInitString = strdup(buf);
838 EscapeExpand(buf, appData.firstComputerString);
839 appData.firstComputerString = strdup(buf);
840 EscapeExpand(buf, appData.secondComputerString);
841 appData.secondComputerString = strdup(buf);
844 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
847 if (chdir(chessDir) != 0) {
848 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
854 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
855 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
856 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
857 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
860 setbuf(debugFP, NULL);
864 if (appData.debugMode) {
865 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
869 /* [HGM,HR] make sure board size is acceptable */
870 if(appData.NrFiles > BOARD_FILES ||
871 appData.NrRanks > BOARD_RANKS )
872 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
875 /* This feature does not work; animation needs a rewrite */
876 appData.highlightDragging = FALSE;
880 gameInfo.variant = StringToVariant(appData.variant);
884 * determine size, based on supplied or remembered -size, or screen size
886 if (isdigit(appData.boardSize[0])) {
887 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
888 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
889 &fontPxlSize, &smallLayout, &tinyLayout);
891 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
892 programName, appData.boardSize);
896 /* Find some defaults; use the nearest known size */
897 SizeDefaults *szd, *nearest;
898 int distance = 99999;
899 nearest = szd = sizeDefaults;
900 while (szd->name != NULL) {
901 if (abs(szd->squareSize - squareSize) < distance) {
903 distance = abs(szd->squareSize - squareSize);
904 if (distance == 0) break;
908 if (i < 2) lineGap = nearest->lineGap;
909 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
910 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
911 if (i < 5) fontPxlSize = nearest->fontPxlSize;
912 if (i < 6) smallLayout = nearest->smallLayout;
913 if (i < 7) tinyLayout = nearest->tinyLayout;
916 SizeDefaults *szd = sizeDefaults;
917 if (*appData.boardSize == NULLCHAR) {
918 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
919 guint screenwidth = gdk_screen_get_width(screen);
920 guint screenheight = gdk_screen_get_height(screen);
921 while (screenwidth < szd->minScreenSize ||
922 screenheight < szd->minScreenSize) {
925 if (szd->name == NULL) szd--;
926 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
928 while (szd->name != NULL &&
929 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
930 if (szd->name == NULL) {
931 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
932 programName, appData.boardSize);
936 squareSize = szd->squareSize;
937 lineGap = szd->lineGap;
938 clockFontPxlSize = szd->clockFontPxlSize;
939 coordFontPxlSize = szd->coordFontPxlSize;
940 fontPxlSize = szd->fontPxlSize;
941 smallLayout = szd->smallLayout;
942 tinyLayout = szd->tinyLayout;
943 // [HGM] font: use defaults from settings file if available and not overruled
946 defaultLineGap = lineGap;
947 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
949 /* [HR] height treated separately (hacked) */
950 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
951 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
954 * Determine what fonts to use.
957 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
961 * Detect if there are not enough colors available and adapt.
964 if (DefaultDepth(xDisplay, xScreen) <= 2) {
965 appData.monoMode = True;
969 forceMono = MakeColors();
972 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
974 appData.monoMode = True;
977 ParseIcsTextColors();
983 layoutName = "tinyLayout";
984 } else if (smallLayout) {
985 layoutName = "smallLayout";
987 layoutName = "normalLayout";
990 wpMain.width = -1; // prevent popup sizes window
991 optList = BoardPopUp(squareSize, lineGap, (void*)
1001 InitDrawingHandle(optList + W_BOARD);
1002 shellWidget = shells[BoardWindow];
1003 currBoard = &optList[W_BOARD];
1004 boardWidget = optList[W_BOARD].handle;
1005 menuBarWidget = optList[W_MENU].handle;
1006 dropMenu = optList[W_DROP].handle;
1007 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1009 formWidget = XtParent(boardWidget);
1010 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1011 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1012 XtGetValues(optList[W_WHITE].handle, args, 2);
1013 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1014 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1015 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1016 XtGetValues(optList[W_PAUSE].handle, args, 2);
1020 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1021 // not need to go into InitDrawingSizes().
1025 // add accelerators to main shell
1026 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1029 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1031 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
1032 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
1033 mainwindowIcon = WhiteIcon;
1034 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1038 * Create a cursor for the board widget.
1041 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1042 XChangeWindowAttributes(xDisplay, xBoardWindow,
1043 CWCursor, &window_attributes);
1047 * Inhibit shell resizing.
1050 shellArgs[0].value = (XtArgVal) &w;
1051 shellArgs[1].value = (XtArgVal) &h;
1052 XtGetValues(shellWidget, shellArgs, 2);
1053 shellArgs[4].value = shellArgs[2].value = w;
1054 shellArgs[5].value = shellArgs[3].value = h;
1055 // XtSetValues(shellWidget, &shellArgs[2], 4);
1058 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1059 // It wil only become known asynchronously, when we first write a string into it.
1060 // This will then change the clock widget height, which triggers resizing the top-level window
1061 // and a configure event. Only then can we know the total height of the top-level window,
1062 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1063 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1066 gtk_widget_get_allocation(shells[BoardWindow], &a);
1067 w = a.width; h = a.height;
1068 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1069 clockKludge = hc = a.height;
1070 gtk_widget_get_allocation(boardWidget, &a);
1071 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1072 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1078 if(appData.logoSize)
1079 { // locate and read user logo
1081 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1082 ASSIGN(userLogo, buf);
1085 if (appData.animate || appData.animateDragging)
1088 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1089 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1091 /* [AS] Restore layout */
1092 if( wpMoveHistory.visible ) {
1096 if( wpEvalGraph.visible )
1101 if( wpEngineOutput.visible ) {
1102 EngineOutputPopUp();
1107 if (errorExitStatus == -1) {
1108 if (appData.icsActive) {
1109 /* We now wait until we see "login:" from the ICS before
1110 sending the logon script (problems with timestamp otherwise) */
1111 /*ICSInitScript();*/
1112 if (appData.icsInputBox) ICSInputBoxPopUp();
1116 signal(SIGWINCH, TermSizeSigHandler);
1118 signal(SIGINT, IntSigHandler);
1119 signal(SIGTERM, IntSigHandler);
1120 if (*appData.cmailGameName != NULLCHAR) {
1121 signal(SIGUSR1, CmailSigHandler);
1125 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1128 // XtSetKeyboardFocus(shellWidget, formWidget);
1130 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1133 /* check for GTK events and process them */
1136 gtk_main_iteration();
1139 if (appData.debugMode) fclose(debugFP); // [DM] debug
1144 TermSizeSigHandler (int sig)
1150 IntSigHandler (int sig)
1156 CmailSigHandler (int sig)
1161 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1163 /* Activate call-back function CmailSigHandlerCallBack() */
1164 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1166 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1170 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1173 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1175 /**** end signal code ****/
1178 #define Abs(n) ((n)<0 ? -(n) : (n))
1182 InsertPxlSize (char *pattern, int targetPxlSize)
1184 char *base_fnt_lst, strInt[12], *p, *q;
1185 int alternatives, i, len, strIntLen;
1188 * Replace the "*" (if present) in the pixel-size slot of each
1189 * alternative with the targetPxlSize.
1193 while ((p = strchr(p, ',')) != NULL) {
1197 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1198 strIntLen = strlen(strInt);
1199 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1203 while (alternatives--) {
1204 char *comma = strchr(p, ',');
1205 for (i=0; i<14; i++) {
1206 char *hyphen = strchr(p, '-');
1208 if (comma && hyphen > comma) break;
1209 len = hyphen + 1 - p;
1210 if (i == 7 && *p == '*' && len == 2) {
1212 memcpy(q, strInt, strIntLen);
1222 len = comma + 1 - p;
1229 return base_fnt_lst;
1234 CreateFontSet (char *base_fnt_lst)
1237 char **missing_list;
1241 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1242 &missing_list, &missing_count, &def_string);
1243 if (appData.debugMode) {
1245 XFontStruct **font_struct_list;
1246 char **font_name_list;
1247 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1249 fprintf(debugFP, " got list %s, locale %s\n",
1250 XBaseFontNameListOfFontSet(fntSet),
1251 XLocaleOfFontSet(fntSet));
1252 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1253 for (i = 0; i < count; i++) {
1254 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1257 for (i = 0; i < missing_count; i++) {
1258 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1261 if (fntSet == NULL) {
1262 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1268 #else // not ENABLE_NLS
1270 * Find a font that matches "pattern" that is as close as
1271 * possible to the targetPxlSize. Prefer fonts that are k
1272 * pixels smaller to fonts that are k pixels larger. The
1273 * pattern must be in the X Consortium standard format,
1274 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1275 * The return value should be freed with XtFree when no
1279 FindFont (char *pattern, int targetPxlSize)
1281 char **fonts, *p, *best, *scalable, *scalableTail;
1282 int i, j, nfonts, minerr, err, pxlSize;
1285 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1287 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1288 programName, pattern);
1295 for (i=0; i<nfonts; i++) {
1298 if (*p != '-') continue;
1300 if (*p == NULLCHAR) break;
1301 if (*p++ == '-') j++;
1303 if (j < 7) continue;
1306 scalable = fonts[i];
1309 err = pxlSize - targetPxlSize;
1310 if (Abs(err) < Abs(minerr) ||
1311 (minerr > 0 && err < 0 && -err == minerr)) {
1317 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1318 /* If the error is too big and there is a scalable font,
1319 use the scalable font. */
1320 int headlen = scalableTail - scalable;
1321 p = (char *) XtMalloc(strlen(scalable) + 10);
1322 while (isdigit(*scalableTail)) scalableTail++;
1323 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1325 p = (char *) XtMalloc(strlen(best) + 2);
1326 safeStrCpy(p, best, strlen(best)+1 );
1328 if (appData.debugMode) {
1329 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1330 pattern, targetPxlSize, p);
1332 XFreeFontNames(fonts);
1339 EnableNamedMenuItem (char *menuRef, int state)
1341 MenuItem *item = MenuNameToItem(menuRef);
1343 if(item) gtk_widget_set_sensitive(item->handle, state);
1347 EnableButtonBar (int state)
1350 XtSetSensitive(optList[W_BUTTON].handle, state);
1356 SetMenuEnables (Enables *enab)
1358 while (enab->name != NULL) {
1359 EnableNamedMenuItem(enab->name, enab->value);
1364 gboolean KeyPressProc(window, eventkey, data)
1366 GdkEventKey *eventkey;
1370 MoveTypeInProc(eventkey); // pop up for typed in moves
1373 /* check for other key values */
1374 switch(eventkey->keyval) {
1386 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1387 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1389 if(*nprms == 0) return;
1390 item = MenuNameToItem(prms[0]);
1391 if(item) ((MenuProc *) item->proc) ();
1405 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1406 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1407 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1408 dmEnables[i].piece);
1409 XtSetSensitive(entry, p != NULL || !appData.testLegality
1410 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1411 && !appData.icsActive));
1413 while (p && *p++ == dmEnables[i].piece) count++;
1414 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1416 XtSetArg(args[j], XtNlabel, label); j++;
1417 XtSetValues(entry, args, j);
1423 do_flash_delay (unsigned long msec)
1429 FlashDelay (int flash_delay)
1431 if(flash_delay) do_flash_delay(flash_delay);
1435 Fraction (int x, int start, int stop)
1437 double f = ((double) x - start)/(stop - start);
1438 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1442 static WindowPlacement wpNew;
1445 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1447 int touch=0, fudge = 2, f = 2;
1448 GetActualPlacement(sh, wp);
1449 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1450 if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x) < fudge) touch = 2; else // left touch
1451 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1452 if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y) < fudge) touch = 4; // top touch
1453 //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);
1454 if(!touch ) return; // only windows that touch co-move
1455 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1456 int heightInc = wpNew.height - wpMain.height;
1457 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1458 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1459 wp->y += fracTop * heightInc;
1460 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1462 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1464 wp->height += heightInc;
1465 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1466 int widthInc = wpNew.width - wpMain.width;
1467 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1468 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1469 wp->y += fracLeft * widthInc;
1470 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1472 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1474 wp->width += widthInc;
1476 wp->x += wpNew.x - wpMain.x;
1477 wp->y += wpNew.y - wpMain.y;
1478 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1479 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1481 XtSetArg(args[j], XtNx, wp->x); j++;
1482 XtSetArg(args[j], XtNy, wp->y); j++;
1483 XtSetValues(sh, args, j);
1485 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1486 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1487 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1491 ReSize (WindowPlacement *wp)
1494 int sqx, sqy, w, h, hc, lg = lineGap;
1495 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1496 hc = a.height; // clock height can depend on single / double line clock text!
1497 if(clockKludge == a.height) return; // wait for clock to get final size at startup
1498 if(clockKludge) { // clock height OK now; calculate desired initial board height
1500 wp->height = BOARD_HEIGHT * (squareSize + lineGap) + lineGap + marginH + hc;
1502 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1503 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1504 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1505 if(sqy < sqx) sqx = sqy;
1506 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1507 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1508 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1509 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1510 if(sqy < sqx) sqx = sqy;
1512 if(sqx != squareSize) {
1513 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1514 squareSize = sqx; // adopt new square size
1515 CreatePNGPieces(); // make newly scaled pieces
1516 InitDrawingSizes(0, 0); // creates grid etc.
1517 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1518 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1519 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1520 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1521 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1524 static guint delayedDragTag = 0;
1533 // GetActualPlacement(shellWidget, &wpNew);
1534 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1535 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1536 busy = 0; return; // false alarm
1539 if(appData.useStickyWindows) {
1540 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1541 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1542 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1543 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1546 DrawPosition(True, NULL);
1547 if(delayedDragTag) g_source_remove(delayedDragTag);
1548 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1555 //printf("old timr = %d\n", delayedDragTag);
1556 if(delayedDragTag) g_source_remove(delayedDragTag);
1557 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1558 //printf("new timr = %d\n", delayedDragTag);
1562 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1564 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1566 wpNew.x = event->configure.x;
1567 wpNew.y = event->configure.y;
1568 wpNew.width = event->configure.width;
1569 wpNew.height = event->configure.height;
1570 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1576 /* Disable all user input other than deleting the window */
1577 static int frozen = 0;
1583 /* Grab by a widget that doesn't accept input */
1584 gtk_grab_add(optList[W_MESSG].handle);
1588 /* Undo a FreezeUI */
1592 if (!frozen) return;
1593 gtk_grab_remove(optList[W_MESSG].handle);
1600 static int oldPausing = FALSE;
1601 static GameMode oldmode = (GameMode) -1;
1603 if (!boardWidget) return;
1605 if (pausing != oldPausing) {
1606 oldPausing = pausing;
1607 MarkMenuItem("Mode.Pause", pausing);
1609 if (appData.showButtonBar) {
1610 /* Always toggle, don't set. Previous code messes up when
1611 invoked while the button is pressed, as releasing it
1612 toggles the state again. */
1614 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1615 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1619 wname = ModeToWidgetName(oldmode);
1620 if (wname != NULL) {
1621 MarkMenuItem(wname, False);
1623 wname = ModeToWidgetName(gameMode);
1624 if (wname != NULL) {
1625 MarkMenuItem(wname, True);
1628 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1630 /* Maybe all the enables should be handled here, not just this one */
1631 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1633 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1638 * Button/menu procedures
1641 void CopyFileToClipboard(gchar *filename)
1643 gchar *selection_tmp;
1647 FILE* f = fopen(filename, "r");
1650 if (f == NULL) return;
1654 selection_tmp = g_try_malloc(len + 1);
1655 if (selection_tmp == NULL) {
1656 printf("Malloc failed in CopyFileToClipboard\n");
1659 count = fread(selection_tmp, 1, len, f);
1662 g_free(selection_tmp);
1665 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1667 // copy selection_tmp to clipboard
1668 GdkDisplay *gdisp = gdk_display_get_default();
1670 g_free(selection_tmp);
1673 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1674 gtk_clipboard_set_text(cb, selection_tmp, -1);
1675 g_free(selection_tmp);
1679 CopySomething (char *src)
1681 GdkDisplay *gdisp = gdk_display_get_default();
1683 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1684 if (gdisp == NULL) return;
1685 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1686 gtk_clipboard_set_text(cb, src, -1);
1690 PastePositionProc ()
1692 GdkDisplay *gdisp = gdk_display_get_default();
1696 if (gdisp == NULL) return;
1697 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1698 fenstr = gtk_clipboard_wait_for_text(cb);
1699 if (fenstr==NULL) return; // nothing had been selected to copy
1700 EditPositionPasteFEN(fenstr);
1712 // get game from clipboard
1713 GdkDisplay *gdisp = gdk_display_get_default();
1714 if (gdisp == NULL) return;
1715 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1716 text = gtk_clipboard_wait_for_text(cb);
1717 if (text == NULL) return; // nothing to paste
1720 // write to temp file
1721 if (text == NULL || len == 0) {
1722 return; //nothing to paste
1724 f = fopen(gamePasteFilename, "w");
1726 DisplayError(_("Can't open temp file"), errno);
1729 fwrite(text, 1, len, f);
1733 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1740 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1746 void MoveTypeInProc(eventkey)
1747 GdkEventKey *eventkey;
1751 // ingnore if ctrl, alt, or meta is pressed
1752 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1756 buf[0]=eventkey->keyval;
1758 if (eventkey->keyval > 32 && eventkey->keyval < 256)
1764 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1766 if (!TempBackwardActive) {
1767 TempBackwardActive = True;
1773 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1775 /* Check to see if triggered by a key release event for a repeating key.
1776 * If so the next queued event will be a key press of the same key at the same time */
1777 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1779 XPeekEvent(xDisplay, &next);
1780 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1781 next.xkey.keycode == event->xkey.keycode)
1785 TempBackwardActive = False;
1791 { // called from menu
1793 system("%s ./man.command", appData.sysOpen);
1795 system("xterm -e man xboard &");
1800 SetWindowTitle (char *text, char *title, char *icon)
1805 if (appData.titleInWindow) {
1807 XtSetArg(args[i], XtNlabel, text); i++;
1808 XtSetValues(titleWidget, args, i);
1811 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1812 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1813 XtSetValues(shellWidget, args, i);
1814 XSync(xDisplay, False);
1816 if (appData.titleInWindow) {
1817 SetWidgetLabel(titleWidget, text);
1819 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1824 DisplayIcsInteractionTitle (String message)
1827 if (oldICSInteractionTitle == NULL) {
1828 /* Magic to find the old window title, adapted from vim */
1829 char *wina = getenv("WINDOWID");
1831 Window win = (Window) atoi(wina);
1832 Window root, parent, *children;
1833 unsigned int nchildren;
1834 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1836 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1837 if (!XQueryTree(xDisplay, win, &root, &parent,
1838 &children, &nchildren)) break;
1839 if (children) XFree((void *)children);
1840 if (parent == root || parent == 0) break;
1843 XSetErrorHandler(oldHandler);
1845 if (oldICSInteractionTitle == NULL) {
1846 oldICSInteractionTitle = "xterm";
1849 printf("\033]0;%s\007", message);
1856 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1858 GtkWidget *w = (GtkWidget *) opt->handle;
1865 strcpy(bgcolor, "black");
1866 strcpy(fgcolor, "white");
1868 strcpy(bgcolor, "white");
1869 strcpy(fgcolor, "black");
1872 appData.lowTimeWarning &&
1873 (timer / 1000) < appData.icsAlarmTime) {
1874 strcpy(fgcolor, appData.lowTimeWarningColor);
1877 gdk_color_parse( bgcolor, &col );
1878 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1880 if (appData.clockMode) {
1881 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1882 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1884 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
1885 bgcolor, fgcolor, color);
1887 gtk_label_set_markup(GTK_LABEL(w), markup);
1891 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1894 SetClockIcon (int color)
1896 GdkPixbuf *pm = *clockIcons[color];
1897 if (mainwindowIcon != pm) {
1898 mainwindowIcon = pm;
1899 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1903 #define INPUT_SOURCE_BUF_SIZE 8192
1912 char buf[INPUT_SOURCE_BUF_SIZE];
1917 DoInputCallback(io, cond, data)
1922 /* read input from one of the input source (for example a chess program, ICS, etc).
1923 * and call a function that will handle the input
1930 /* All information (callback function, file descriptor, etc) is
1931 * saved in an InputSource structure
1933 InputSource *is = (InputSource *) data;
1935 if (is->lineByLine) {
1936 count = read(is->fd, is->unused,
1937 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1939 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1942 is->unused += count;
1944 /* break input into lines and call the callback function on each
1947 while (p < is->unused) {
1948 q = memchr(p, '\n', is->unused - p);
1949 if (q == NULL) break;
1951 (is->func)(is, is->closure, p, q - p, 0);
1954 /* remember not yet used part of the buffer */
1956 while (p < is->unused) {
1961 /* read maximum length of input buffer and send the whole buffer
1962 * to the callback function
1964 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1969 (is->func)(is, is->closure, is->buf, count, error);
1971 return True; // Must return true or the watch will be removed
1974 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1981 GIOChannel *channel;
1982 ChildProc *cp = (ChildProc *) pr;
1984 is = (InputSource *) calloc(1, sizeof(InputSource));
1985 is->lineByLine = lineByLine;
1989 is->fd = fileno(stdin);
1991 is->kind = cp->kind;
1992 is->fd = cp->fdFrom;
1995 is->unused = is->buf;
1999 /* GTK-TODO: will this work on windows?*/
2001 channel = g_io_channel_unix_new(is->fd);
2002 g_io_channel_set_close_on_unref (channel, TRUE);
2003 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2005 is->closure = closure;
2006 return (InputSourceRef) is;
2011 RemoveInputSource(isr)
2014 InputSource *is = (InputSource *) isr;
2016 if (is->sid == 0) return;
2017 g_source_remove(is->sid);
2024 static Boolean frameWaiting;
2027 FrameAlarm (int sig)
2029 frameWaiting = False;
2030 /* In case System-V style signals. Needed?? */
2031 signal(SIGALRM, FrameAlarm);
2035 FrameDelay (int time)
2037 struct itimerval delay;
2040 frameWaiting = True;
2041 signal(SIGALRM, FrameAlarm);
2042 delay.it_interval.tv_sec =
2043 delay.it_value.tv_sec = time / 1000;
2044 delay.it_interval.tv_usec =
2045 delay.it_value.tv_usec = (time % 1000) * 1000;
2046 setitimer(ITIMER_REAL, &delay, NULL);
2047 while (frameWaiting) pause();
2048 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2049 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2050 setitimer(ITIMER_REAL, &delay, NULL);
2057 FrameDelay (int time)
2060 XSync(xDisplay, False);
2062 // gtk_main_iteration_do(False);
2065 usleep(time * 1000);
2071 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2073 char buf[MSG_SIZ], *logoName = buf;
2074 if(appData.logo[n][0]) {
2075 logoName = appData.logo[n];
2076 } else if(appData.autoLogo) {
2077 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2078 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2079 } else if(appData.directory[n] && appData.directory[n][0]) {
2080 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2084 { ASSIGN(cps->programLogo, logoName); }
2088 UpdateLogos (int displ)
2090 if(optList[W_WHITE-1].handle == NULL) return;
2091 LoadLogo(&first, 0, 0);
2092 LoadLogo(&second, 1, appData.icsActive);
2093 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2097 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2108 GtkFileFilter *gtkfilter;
2109 GtkFileFilter *gtkfilter_all;
2111 char fileext[10] = "";
2112 char *result = NULL;
2115 /* make a copy of the filter string, so that strtok can work with it*/
2116 cp = strdup(filter);
2118 /* add filters for file extensions */
2119 gtkfilter = gtk_file_filter_new();
2120 gtkfilter_all = gtk_file_filter_new();
2122 /* one filter to show everything */
2123 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2124 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2126 /* add filter if present */
2127 result = strtok(cp, space);
2128 while( result != NULL ) {
2129 snprintf(fileext,10,"*%s",result);
2130 result = strtok( NULL, space );
2131 gtk_file_filter_add_pattern(gtkfilter, fileext);
2134 /* second filter to only show what's useful */
2135 gtk_file_filter_set_name (gtkfilter,filter);
2137 if (openMode[0] == 'r')
2139 dialog = gtk_file_chooser_dialog_new (label,
2141 GTK_FILE_CHOOSER_ACTION_OPEN,
2142 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2143 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2148 dialog = gtk_file_chooser_dialog_new (label,
2150 GTK_FILE_CHOOSER_ACTION_SAVE,
2151 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2152 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2154 /* add filename suggestions */
2155 if (strlen(def) > 0 )
2156 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2158 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2162 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2163 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2164 /* activate filter */
2165 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2167 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2172 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2175 f = fopen(filename, openMode);
2178 DisplayError(_("Failed to open file"), errno);
2182 /* TODO add indec */
2184 ASSIGN(*name, filename);
2185 ScheduleDelayedEvent(DelayedLoad, 50);
2190 gtk_widget_destroy (dialog);