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/Contents/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 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
804 typedef struct {char *name, *value; } Config;
805 static Config configList[] = {
806 { "Datadir", DATADIR },
807 { "Sysconfdir", SYSCONFDIR },
812 for(i=0; configList[i].name; i++) {
813 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
814 if(argc > 2) printf("%s", configList[i].value);
815 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
820 /* set up keyboard accelerators group */
821 GtkAccelerators = gtk_accel_group_new();
823 programName = strrchr(argv[0], '/');
824 if (programName == NULL)
825 programName = argv[0];
830 // if (appData.debugMode) {
831 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
834 bindtextdomain(PACKAGE, LOCALEDIR);
835 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
839 appData.boardSize = "";
840 InitAppData(ConvertToLine(argc, argv));
842 if (p == NULL) p = "/tmp";
843 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
844 gameCopyFilename = (char*) malloc(i);
845 gamePasteFilename = (char*) malloc(i);
846 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
847 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
849 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
850 static char buf[MSG_SIZ];
851 snprintf(buf, MSG_SIZ, appData.sysOpen, DATADIR);
852 ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
853 EscapeExpand(buf, appData.firstInitString);
854 appData.firstInitString = strdup(buf);
855 EscapeExpand(buf, appData.secondInitString);
856 appData.secondInitString = strdup(buf);
857 EscapeExpand(buf, appData.firstComputerString);
858 appData.firstComputerString = strdup(buf);
859 EscapeExpand(buf, appData.secondComputerString);
860 appData.secondComputerString = strdup(buf);
863 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
866 if (chdir(chessDir) != 0) {
867 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
873 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
874 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
875 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
876 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
879 setbuf(debugFP, NULL);
883 if (appData.debugMode) {
884 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
888 /* [HGM,HR] make sure board size is acceptable */
889 if(appData.NrFiles > BOARD_FILES ||
890 appData.NrRanks > BOARD_RANKS )
891 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
894 /* This feature does not work; animation needs a rewrite */
895 appData.highlightDragging = FALSE;
899 gameInfo.variant = StringToVariant(appData.variant);
903 * determine size, based on supplied or remembered -size, or screen size
905 if (isdigit(appData.boardSize[0])) {
906 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
907 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
908 &fontPxlSize, &smallLayout, &tinyLayout);
910 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
911 programName, appData.boardSize);
915 /* Find some defaults; use the nearest known size */
916 SizeDefaults *szd, *nearest;
917 int distance = 99999;
918 nearest = szd = sizeDefaults;
919 while (szd->name != NULL) {
920 if (abs(szd->squareSize - squareSize) < distance) {
922 distance = abs(szd->squareSize - squareSize);
923 if (distance == 0) break;
927 if (i < 2) lineGap = nearest->lineGap;
928 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
929 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
930 if (i < 5) fontPxlSize = nearest->fontPxlSize;
931 if (i < 6) smallLayout = nearest->smallLayout;
932 if (i < 7) tinyLayout = nearest->tinyLayout;
935 SizeDefaults *szd = sizeDefaults;
936 if (*appData.boardSize == NULLCHAR) {
937 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
938 guint screenwidth = gdk_screen_get_width(screen);
939 guint screenheight = gdk_screen_get_height(screen);
940 while (screenwidth < szd->minScreenSize ||
941 screenheight < szd->minScreenSize) {
944 if (szd->name == NULL) szd--;
945 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
947 while (szd->name != NULL &&
948 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
949 if (szd->name == NULL) {
950 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
951 programName, appData.boardSize);
955 squareSize = szd->squareSize;
956 lineGap = szd->lineGap;
957 clockFontPxlSize = szd->clockFontPxlSize;
958 coordFontPxlSize = szd->coordFontPxlSize;
959 fontPxlSize = szd->fontPxlSize;
960 smallLayout = szd->smallLayout;
961 tinyLayout = szd->tinyLayout;
962 // [HGM] font: use defaults from settings file if available and not overruled
965 defaultLineGap = lineGap;
966 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
968 /* [HR] height treated separately (hacked) */
969 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
970 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
973 * Determine what fonts to use.
976 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
980 * Detect if there are not enough colors available and adapt.
983 if (DefaultDepth(xDisplay, xScreen) <= 2) {
984 appData.monoMode = True;
988 forceMono = MakeColors();
991 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
993 appData.monoMode = True;
996 ParseIcsTextColors();
1002 layoutName = "tinyLayout";
1003 } else if (smallLayout) {
1004 layoutName = "smallLayout";
1006 layoutName = "normalLayout";
1009 wpMain.width = -1; // prevent popup sizes window
1010 optList = BoardPopUp(squareSize, lineGap, (void*)
1020 InitDrawingHandle(optList + W_BOARD);
1021 shellWidget = shells[BoardWindow];
1022 currBoard = &optList[W_BOARD];
1023 boardWidget = optList[W_BOARD].handle;
1024 menuBarWidget = optList[W_MENU].handle;
1025 dropMenu = optList[W_DROP].handle;
1026 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1028 formWidget = XtParent(boardWidget);
1029 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1030 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1031 XtGetValues(optList[W_WHITE].handle, args, 2);
1032 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1033 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1034 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1035 XtGetValues(optList[W_PAUSE].handle, args, 2);
1039 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1040 // not need to go into InitDrawingSizes().
1044 // add accelerators to main shell
1045 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1048 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1050 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
1051 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
1052 mainwindowIcon = WhiteIcon;
1053 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1057 * Create a cursor for the board widget.
1060 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1061 XChangeWindowAttributes(xDisplay, xBoardWindow,
1062 CWCursor, &window_attributes);
1066 * Inhibit shell resizing.
1069 shellArgs[0].value = (XtArgVal) &w;
1070 shellArgs[1].value = (XtArgVal) &h;
1071 XtGetValues(shellWidget, shellArgs, 2);
1072 shellArgs[4].value = shellArgs[2].value = w;
1073 shellArgs[5].value = shellArgs[3].value = h;
1074 // XtSetValues(shellWidget, &shellArgs[2], 4);
1077 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1078 // It wil only become known asynchronously, when we first write a string into it.
1079 // This will then change the clock widget height, which triggers resizing the top-level window
1080 // and a configure event. Only then can we know the total height of the top-level window,
1081 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1082 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1085 gtk_widget_get_allocation(shells[BoardWindow], &a);
1086 w = a.width; h = a.height;
1087 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1088 clockKludge = hc = a.height;
1089 gtk_widget_get_allocation(boardWidget, &a);
1090 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1091 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1097 if(appData.logoSize)
1098 { // locate and read user logo
1100 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1101 ASSIGN(userLogo, buf);
1104 if (appData.animate || appData.animateDragging)
1107 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1108 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1110 /* [AS] Restore layout */
1111 if( wpMoveHistory.visible ) {
1115 if( wpEvalGraph.visible )
1120 if( wpEngineOutput.visible ) {
1121 EngineOutputPopUp();
1126 if (errorExitStatus == -1) {
1127 if (appData.icsActive) {
1128 /* We now wait until we see "login:" from the ICS before
1129 sending the logon script (problems with timestamp otherwise) */
1130 /*ICSInitScript();*/
1131 if (appData.icsInputBox) ICSInputBoxPopUp();
1135 signal(SIGWINCH, TermSizeSigHandler);
1137 signal(SIGINT, IntSigHandler);
1138 signal(SIGTERM, IntSigHandler);
1139 if (*appData.cmailGameName != NULLCHAR) {
1140 signal(SIGUSR1, CmailSigHandler);
1144 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1147 // XtSetKeyboardFocus(shellWidget, formWidget);
1149 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1152 /* check for GTK events and process them */
1155 gtk_main_iteration();
1158 if (appData.debugMode) fclose(debugFP); // [DM] debug
1163 TermSizeSigHandler (int sig)
1169 IntSigHandler (int sig)
1175 CmailSigHandler (int sig)
1180 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1182 /* Activate call-back function CmailSigHandlerCallBack() */
1183 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1185 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1189 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1192 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1194 /**** end signal code ****/
1197 #define Abs(n) ((n)<0 ? -(n) : (n))
1201 InsertPxlSize (char *pattern, int targetPxlSize)
1203 char *base_fnt_lst, strInt[12], *p, *q;
1204 int alternatives, i, len, strIntLen;
1207 * Replace the "*" (if present) in the pixel-size slot of each
1208 * alternative with the targetPxlSize.
1212 while ((p = strchr(p, ',')) != NULL) {
1216 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1217 strIntLen = strlen(strInt);
1218 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1222 while (alternatives--) {
1223 char *comma = strchr(p, ',');
1224 for (i=0; i<14; i++) {
1225 char *hyphen = strchr(p, '-');
1227 if (comma && hyphen > comma) break;
1228 len = hyphen + 1 - p;
1229 if (i == 7 && *p == '*' && len == 2) {
1231 memcpy(q, strInt, strIntLen);
1241 len = comma + 1 - p;
1248 return base_fnt_lst;
1253 CreateFontSet (char *base_fnt_lst)
1256 char **missing_list;
1260 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1261 &missing_list, &missing_count, &def_string);
1262 if (appData.debugMode) {
1264 XFontStruct **font_struct_list;
1265 char **font_name_list;
1266 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1268 fprintf(debugFP, " got list %s, locale %s\n",
1269 XBaseFontNameListOfFontSet(fntSet),
1270 XLocaleOfFontSet(fntSet));
1271 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1272 for (i = 0; i < count; i++) {
1273 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1276 for (i = 0; i < missing_count; i++) {
1277 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1280 if (fntSet == NULL) {
1281 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1287 #else // not ENABLE_NLS
1289 * Find a font that matches "pattern" that is as close as
1290 * possible to the targetPxlSize. Prefer fonts that are k
1291 * pixels smaller to fonts that are k pixels larger. The
1292 * pattern must be in the X Consortium standard format,
1293 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1294 * The return value should be freed with XtFree when no
1298 FindFont (char *pattern, int targetPxlSize)
1300 char **fonts, *p, *best, *scalable, *scalableTail;
1301 int i, j, nfonts, minerr, err, pxlSize;
1304 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1306 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1307 programName, pattern);
1314 for (i=0; i<nfonts; i++) {
1317 if (*p != '-') continue;
1319 if (*p == NULLCHAR) break;
1320 if (*p++ == '-') j++;
1322 if (j < 7) continue;
1325 scalable = fonts[i];
1328 err = pxlSize - targetPxlSize;
1329 if (Abs(err) < Abs(minerr) ||
1330 (minerr > 0 && err < 0 && -err == minerr)) {
1336 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1337 /* If the error is too big and there is a scalable font,
1338 use the scalable font. */
1339 int headlen = scalableTail - scalable;
1340 p = (char *) XtMalloc(strlen(scalable) + 10);
1341 while (isdigit(*scalableTail)) scalableTail++;
1342 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1344 p = (char *) XtMalloc(strlen(best) + 2);
1345 safeStrCpy(p, best, strlen(best)+1 );
1347 if (appData.debugMode) {
1348 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1349 pattern, targetPxlSize, p);
1351 XFreeFontNames(fonts);
1358 EnableNamedMenuItem (char *menuRef, int state)
1360 MenuItem *item = MenuNameToItem(menuRef);
1362 if(item) gtk_widget_set_sensitive(item->handle, state);
1366 EnableButtonBar (int state)
1369 XtSetSensitive(optList[W_BUTTON].handle, state);
1375 SetMenuEnables (Enables *enab)
1377 while (enab->name != NULL) {
1378 EnableNamedMenuItem(enab->name, enab->value);
1383 gboolean KeyPressProc(window, eventkey, data)
1385 GdkEventKey *eventkey;
1389 MoveTypeInProc(eventkey); // pop up for typed in moves
1392 /* check for other key values */
1393 switch(eventkey->keyval) {
1405 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1406 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1408 if(*nprms == 0) return;
1409 item = MenuNameToItem(prms[0]);
1410 if(item) ((MenuProc *) item->proc) ();
1424 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1425 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1426 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1427 dmEnables[i].piece);
1428 XtSetSensitive(entry, p != NULL || !appData.testLegality
1429 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1430 && !appData.icsActive));
1432 while (p && *p++ == dmEnables[i].piece) count++;
1433 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1435 XtSetArg(args[j], XtNlabel, label); j++;
1436 XtSetValues(entry, args, j);
1442 do_flash_delay (unsigned long msec)
1448 FlashDelay (int flash_delay)
1450 if(flash_delay) do_flash_delay(flash_delay);
1454 Fraction (int x, int start, int stop)
1456 double f = ((double) x - start)/(stop - start);
1457 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1461 static WindowPlacement wpNew;
1464 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1466 int touch=0, fudge = 2, f = 2;
1467 GetActualPlacement(sh, wp);
1468 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1469 if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x) < fudge) touch = 2; else // left touch
1470 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1471 if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y) < fudge) touch = 4; // top touch
1472 //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);
1473 if(!touch ) return; // only windows that touch co-move
1474 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1475 int heightInc = wpNew.height - wpMain.height;
1476 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1477 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1478 wp->y += fracTop * heightInc;
1479 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1481 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1483 wp->height += heightInc;
1484 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1485 int widthInc = wpNew.width - wpMain.width;
1486 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1487 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1488 wp->y += fracLeft * widthInc;
1489 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1491 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1493 wp->width += widthInc;
1495 wp->x += wpNew.x - wpMain.x;
1496 wp->y += wpNew.y - wpMain.y;
1497 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1498 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1500 XtSetArg(args[j], XtNx, wp->x); j++;
1501 XtSetArg(args[j], XtNy, wp->y); j++;
1502 XtSetValues(sh, args, j);
1504 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1505 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1506 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1510 ReSize (WindowPlacement *wp)
1513 int sqx, sqy, w, h, hc, lg = lineGap;
1514 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1515 hc = a.height; // clock height can depend on single / double line clock text!
1516 if(clockKludge == a.height) return; // wait for clock to get final size at startup
1517 if(clockKludge) { // clock height OK now; calculate desired initial board height
1519 wp->height = BOARD_HEIGHT * (squareSize + lineGap) + lineGap + marginH + hc;
1521 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1522 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1523 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1524 if(sqy < sqx) sqx = sqy;
1525 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1526 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1527 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1528 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1529 if(sqy < sqx) sqx = sqy;
1531 if(sqx != squareSize) {
1532 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1533 squareSize = sqx; // adopt new square size
1534 CreatePNGPieces(); // make newly scaled pieces
1535 InitDrawingSizes(0, 0); // creates grid etc.
1536 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1537 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1538 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1539 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1540 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1543 static guint delayedDragTag = 0;
1552 // GetActualPlacement(shellWidget, &wpNew);
1553 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1554 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1555 busy = 0; return; // false alarm
1558 if(appData.useStickyWindows) {
1559 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1560 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1561 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1562 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1565 DrawPosition(True, NULL);
1566 if(delayedDragTag) g_source_remove(delayedDragTag);
1567 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1574 //printf("old timr = %d\n", delayedDragTag);
1575 if(delayedDragTag) g_source_remove(delayedDragTag);
1576 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1577 //printf("new timr = %d\n", delayedDragTag);
1581 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1583 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1585 wpNew.x = event->configure.x;
1586 wpNew.y = event->configure.y;
1587 wpNew.width = event->configure.width;
1588 wpNew.height = event->configure.height;
1589 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1595 /* Disable all user input other than deleting the window */
1596 static int frozen = 0;
1602 /* Grab by a widget that doesn't accept input */
1603 gtk_grab_add(optList[W_MESSG].handle);
1607 /* Undo a FreezeUI */
1611 if (!frozen) return;
1612 gtk_grab_remove(optList[W_MESSG].handle);
1619 static int oldPausing = FALSE;
1620 static GameMode oldmode = (GameMode) -1;
1622 if (!boardWidget) return;
1624 if (pausing != oldPausing) {
1625 oldPausing = pausing;
1626 MarkMenuItem("Mode.Pause", pausing);
1628 if (appData.showButtonBar) {
1629 /* Always toggle, don't set. Previous code messes up when
1630 invoked while the button is pressed, as releasing it
1631 toggles the state again. */
1633 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1634 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1638 wname = ModeToWidgetName(oldmode);
1639 if (wname != NULL) {
1640 MarkMenuItem(wname, False);
1642 wname = ModeToWidgetName(gameMode);
1643 if (wname != NULL) {
1644 MarkMenuItem(wname, True);
1647 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1649 /* Maybe all the enables should be handled here, not just this one */
1650 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1652 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1657 * Button/menu procedures
1660 void CopyFileToClipboard(gchar *filename)
1662 gchar *selection_tmp;
1666 FILE* f = fopen(filename, "r");
1669 if (f == NULL) return;
1673 selection_tmp = g_try_malloc(len + 1);
1674 if (selection_tmp == NULL) {
1675 printf("Malloc failed in CopyFileToClipboard\n");
1678 count = fread(selection_tmp, 1, len, f);
1681 g_free(selection_tmp);
1684 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1686 // copy selection_tmp to clipboard
1687 GdkDisplay *gdisp = gdk_display_get_default();
1689 g_free(selection_tmp);
1692 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1693 gtk_clipboard_set_text(cb, selection_tmp, -1);
1694 g_free(selection_tmp);
1698 CopySomething (char *src)
1700 GdkDisplay *gdisp = gdk_display_get_default();
1702 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1703 if (gdisp == NULL) return;
1704 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1705 gtk_clipboard_set_text(cb, src, -1);
1709 PastePositionProc ()
1711 GdkDisplay *gdisp = gdk_display_get_default();
1715 if (gdisp == NULL) return;
1716 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1717 fenstr = gtk_clipboard_wait_for_text(cb);
1718 if (fenstr==NULL) return; // nothing had been selected to copy
1719 EditPositionPasteFEN(fenstr);
1731 // get game from clipboard
1732 GdkDisplay *gdisp = gdk_display_get_default();
1733 if (gdisp == NULL) return;
1734 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1735 text = gtk_clipboard_wait_for_text(cb);
1736 if (text == NULL) return; // nothing to paste
1739 // write to temp file
1740 if (text == NULL || len == 0) {
1741 return; //nothing to paste
1743 f = fopen(gamePasteFilename, "w");
1745 DisplayError(_("Can't open temp file"), errno);
1748 fwrite(text, 1, len, f);
1752 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1759 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1765 void MoveTypeInProc(eventkey)
1766 GdkEventKey *eventkey;
1770 // ingnore if ctrl, alt, or meta is pressed
1771 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1775 buf[0]=eventkey->keyval;
1777 if (eventkey->keyval > 32 && eventkey->keyval < 256)
1783 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1785 if (!TempBackwardActive) {
1786 TempBackwardActive = True;
1792 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1794 /* Check to see if triggered by a key release event for a repeating key.
1795 * If so the next queued event will be a key press of the same key at the same time */
1796 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1798 XPeekEvent(xDisplay, &next);
1799 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1800 next.xkey.keycode == event->xkey.keycode)
1804 TempBackwardActive = False;
1810 { // called from menu
1813 snprintf(buf, MSG_SIZ, "%s ./man.command", appData.sysOpen);
1816 system("xterm -e man xboard &");
1821 SetWindowTitle (char *text, char *title, char *icon)
1826 if (appData.titleInWindow) {
1828 XtSetArg(args[i], XtNlabel, text); i++;
1829 XtSetValues(titleWidget, args, i);
1832 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1833 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1834 XtSetValues(shellWidget, args, i);
1835 XSync(xDisplay, False);
1837 if (appData.titleInWindow) {
1838 SetWidgetLabel(titleWidget, text);
1840 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1845 DisplayIcsInteractionTitle (String message)
1848 if (oldICSInteractionTitle == NULL) {
1849 /* Magic to find the old window title, adapted from vim */
1850 char *wina = getenv("WINDOWID");
1852 Window win = (Window) atoi(wina);
1853 Window root, parent, *children;
1854 unsigned int nchildren;
1855 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1857 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1858 if (!XQueryTree(xDisplay, win, &root, &parent,
1859 &children, &nchildren)) break;
1860 if (children) XFree((void *)children);
1861 if (parent == root || parent == 0) break;
1864 XSetErrorHandler(oldHandler);
1866 if (oldICSInteractionTitle == NULL) {
1867 oldICSInteractionTitle = "xterm";
1870 printf("\033]0;%s\007", message);
1877 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1879 GtkWidget *w = (GtkWidget *) opt->handle;
1886 strcpy(bgcolor, "black");
1887 strcpy(fgcolor, "white");
1889 strcpy(bgcolor, "white");
1890 strcpy(fgcolor, "black");
1893 appData.lowTimeWarning &&
1894 (timer / 1000) < appData.icsAlarmTime) {
1895 strcpy(fgcolor, appData.lowTimeWarningColor);
1898 gdk_color_parse( bgcolor, &col );
1899 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1901 if (appData.clockMode) {
1902 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1903 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1905 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
1906 bgcolor, fgcolor, color);
1908 gtk_label_set_markup(GTK_LABEL(w), markup);
1912 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1915 SetClockIcon (int color)
1917 GdkPixbuf *pm = *clockIcons[color];
1918 if (mainwindowIcon != pm) {
1919 mainwindowIcon = pm;
1920 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1924 #define INPUT_SOURCE_BUF_SIZE 8192
1933 char buf[INPUT_SOURCE_BUF_SIZE];
1938 DoInputCallback(io, cond, data)
1943 /* read input from one of the input source (for example a chess program, ICS, etc).
1944 * and call a function that will handle the input
1951 /* All information (callback function, file descriptor, etc) is
1952 * saved in an InputSource structure
1954 InputSource *is = (InputSource *) data;
1956 if (is->lineByLine) {
1957 count = read(is->fd, is->unused,
1958 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1960 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1963 is->unused += count;
1965 /* break input into lines and call the callback function on each
1968 while (p < is->unused) {
1969 q = memchr(p, '\n', is->unused - p);
1970 if (q == NULL) break;
1972 (is->func)(is, is->closure, p, q - p, 0);
1975 /* remember not yet used part of the buffer */
1977 while (p < is->unused) {
1982 /* read maximum length of input buffer and send the whole buffer
1983 * to the callback function
1985 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1990 (is->func)(is, is->closure, is->buf, count, error);
1992 return True; // Must return true or the watch will be removed
1995 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2002 GIOChannel *channel;
2003 ChildProc *cp = (ChildProc *) pr;
2005 is = (InputSource *) calloc(1, sizeof(InputSource));
2006 is->lineByLine = lineByLine;
2010 is->fd = fileno(stdin);
2012 is->kind = cp->kind;
2013 is->fd = cp->fdFrom;
2016 is->unused = is->buf;
2020 /* GTK-TODO: will this work on windows?*/
2022 channel = g_io_channel_unix_new(is->fd);
2023 g_io_channel_set_close_on_unref (channel, TRUE);
2024 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2026 is->closure = closure;
2027 return (InputSourceRef) is;
2032 RemoveInputSource(isr)
2035 InputSource *is = (InputSource *) isr;
2037 if (is->sid == 0) return;
2038 g_source_remove(is->sid);
2045 static Boolean frameWaiting;
2048 FrameAlarm (int sig)
2050 frameWaiting = False;
2051 /* In case System-V style signals. Needed?? */
2052 signal(SIGALRM, FrameAlarm);
2056 FrameDelay (int time)
2058 struct itimerval delay;
2061 frameWaiting = True;
2062 signal(SIGALRM, FrameAlarm);
2063 delay.it_interval.tv_sec =
2064 delay.it_value.tv_sec = time / 1000;
2065 delay.it_interval.tv_usec =
2066 delay.it_value.tv_usec = (time % 1000) * 1000;
2067 setitimer(ITIMER_REAL, &delay, NULL);
2068 while (frameWaiting) pause();
2069 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2070 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2071 setitimer(ITIMER_REAL, &delay, NULL);
2078 FrameDelay (int time)
2081 XSync(xDisplay, False);
2083 // gtk_main_iteration_do(False);
2086 usleep(time * 1000);
2092 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2094 char buf[MSG_SIZ], *logoName = buf;
2095 if(appData.logo[n][0]) {
2096 logoName = appData.logo[n];
2097 } else if(appData.autoLogo) {
2098 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2099 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2100 } else if(appData.directory[n] && appData.directory[n][0]) {
2101 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2105 { ASSIGN(cps->programLogo, logoName); }
2109 UpdateLogos (int displ)
2111 if(optList[W_WHITE-1].handle == NULL) return;
2112 LoadLogo(&first, 0, 0);
2113 LoadLogo(&second, 1, appData.icsActive);
2114 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2118 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2129 GtkFileFilter *gtkfilter;
2130 GtkFileFilter *gtkfilter_all;
2132 char fileext[10] = "";
2133 char *result = NULL;
2136 /* make a copy of the filter string, so that strtok can work with it*/
2137 cp = strdup(filter);
2139 /* add filters for file extensions */
2140 gtkfilter = gtk_file_filter_new();
2141 gtkfilter_all = gtk_file_filter_new();
2143 /* one filter to show everything */
2144 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2145 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2147 /* add filter if present */
2148 result = strtok(cp, space);
2149 while( result != NULL ) {
2150 snprintf(fileext,10,"*%s",result);
2151 result = strtok( NULL, space );
2152 gtk_file_filter_add_pattern(gtkfilter, fileext);
2155 /* second filter to only show what's useful */
2156 gtk_file_filter_set_name (gtkfilter,filter);
2158 if (openMode[0] == 'r')
2160 dialog = gtk_file_chooser_dialog_new (label,
2162 GTK_FILE_CHOOSER_ACTION_OPEN,
2163 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2164 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2169 dialog = gtk_file_chooser_dialog_new (label,
2171 GTK_FILE_CHOOSER_ACTION_SAVE,
2172 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2173 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2175 /* add filename suggestions */
2176 if (strlen(def) > 0 )
2177 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2179 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2183 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2184 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2185 /* activate filter */
2186 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2188 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2193 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2196 f = fopen(filename, openMode);
2199 DisplayError(_("Failed to open file"), errno);
2203 /* TODO add indec */
2205 ASSIGN(*name, filename);
2206 ScheduleDelayedEvent(DelayedLoad, 50);
2211 gtk_widget_destroy (dialog);