2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
64 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.h>
69 # if HAVE_SYS_SOCKET_H
70 # include <sys/socket.h>
71 # include <netinet/in.h>
73 # else /* not HAVE_SYS_SOCKET_H */
74 # if HAVE_LAN_SOCKET_H
75 # include <lan/socket.h>
77 # include <lan/netdb.h>
78 # else /* not HAVE_LAN_SOCKET_H */
79 # define OMIT_SOCKETS 1
80 # endif /* not HAVE_LAN_SOCKET_H */
81 # endif /* not HAVE_SYS_SOCKET_H */
82 #endif /* !OMIT_SOCKETS */
87 #else /* not STDC_HEADERS */
88 extern char *getenv();
91 # else /* not HAVE_STRING_H */
93 # endif /* not HAVE_STRING_H */
94 #endif /* not STDC_HEADERS */
97 # include <sys/fcntl.h>
98 #else /* not HAVE_SYS_FCNTL_H */
101 # endif /* HAVE_FCNTL_H */
102 #endif /* not HAVE_SYS_FCNTL_H */
104 #if HAVE_SYS_SYSTEMINFO_H
105 # include <sys/systeminfo.h>
106 #endif /* HAVE_SYS_SYSTEMINFO_H */
108 #if TIME_WITH_SYS_TIME
109 # include <sys/time.h>
113 # include <sys/time.h>
124 # include <sys/wait.h>
129 # define NAMLEN(dirent) strlen((dirent)->d_name)
130 # define HAVE_DIR_STRUCT
132 # define dirent direct
133 # define NAMLEN(dirent) (dirent)->d_namlen
135 # include <sys/ndir.h>
136 # define HAVE_DIR_STRUCT
139 # include <sys/dir.h>
140 # define HAVE_DIR_STRUCT
144 # define HAVE_DIR_STRUCT
152 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
155 #include "frontend.h"
157 #include "backendz.h"
165 #include "engineoutput.h"
175 #define usleep(t) _sleep2(((t)+500)/1000)
179 # define _(s) gettext (s)
180 # define N_(s) gettext_noop (s)
186 int main P((int argc, char **argv));
187 RETSIGTYPE CmailSigHandler P((int sig));
188 RETSIGTYPE IntSigHandler P((int sig));
189 RETSIGTYPE TermSizeSigHandler P((int sig));
191 char *InsertPxlSize P((char *pattern, int targetPxlSize));
192 XFontSet CreateFontSet P((char *base_fnt_lst));
194 char *FindFont P((char *pattern, int targetPxlSize));
196 void DelayedDrag P((void));
197 void ICSInputBoxPopUp P((void));
198 void MoveTypeInProc P((GdkEventKey *eventkey));
199 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
200 Boolean TempBackwardActive = False;
201 void DisplayMove P((int moveNumber));
202 void update_ics_width P(());
203 int CopyMemoProc P(());
204 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
208 XFontSet fontSet, clockFontSet;
211 XFontStruct *clockFontStruct;
213 Font coordFontID, countFontID;
214 XFontStruct *coordFontStruct, *countFontStruct;
216 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
217 GtkWidget *mainwindow;
219 Option *optList; // contains all widgets of main window
222 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
225 static GdkPixbuf *mainwindowIcon=NULL;
226 static GdkPixbuf *WhiteIcon=NULL;
227 static GdkPixbuf *BlackIcon=NULL;
229 /* key board accelerators */
230 GtkAccelGroup *GtkAccelerators;
232 typedef unsigned int BoardSize;
234 Boolean chessProgram;
236 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
237 int smallLayout = 0, tinyLayout = 0,
238 marginW, marginH, // [HGM] for run-time resizing
239 fromX = -1, fromY = -1, toX, toY, commentUp = False,
240 errorExitStatus = -1, defaultLineGap;
242 Dimension textHeight;
244 char *chessDir, *programName, *programVersion;
245 Boolean alwaysOnTop = False;
246 char *icsTextMenuString;
248 char *firstChessProgramNames;
249 char *secondChessProgramNames;
251 WindowPlacement wpMain;
252 WindowPlacement wpConsole;
253 WindowPlacement wpComment;
254 WindowPlacement wpMoveHistory;
255 WindowPlacement wpEvalGraph;
256 WindowPlacement wpEngineOutput;
257 WindowPlacement wpGameList;
258 WindowPlacement wpTags;
259 WindowPlacement wpDualBoard;
261 /* This magic number is the number of intermediate frames used
262 in each half of the animation. For short moves it's reduced
263 by 1. The total number of frames will be factor * 2 + 1. */
266 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
273 DropMenuEnables dmEnables[] = {
282 XtResource clientResources[] = {
283 { "flashCount", "flashCount", XtRInt, sizeof(int),
284 XtOffset(AppDataPtr, flashCount), XtRImmediate,
285 (XtPointer) FLASH_COUNT },
289 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
290 char globalTranslations[] =
291 ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
292 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
293 :<KeyDown>Return: TempBackwardProc() \n \
294 :<KeyUp>Return: TempForwardProc() \n";
296 char ICSInputTranslations[] =
297 "<Key>Up: UpKeyProc() \n "
298 "<Key>Down: DownKeyProc() \n "
299 "<Key>Return: EnterKeyProc() \n";
301 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
302 // as the widget is destroyed before the up-click can call extend-end
303 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
306 String xboardResources[] = {
307 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
315 gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
318 //---------------------------------------------------------------------------------------------------------
319 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
322 #define CW_USEDEFAULT (1<<31)
323 #define ICS_TEXT_MENU_SIZE 90
324 #define DEBUG_FILE "xboard.debug"
325 #define SetCurrentDirectory chdir
326 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
330 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
333 // front-end part of option handling
335 // [HGM] This platform-dependent table provides the location for storing the color info
336 extern char *crWhite, * crBlack;
340 &appData.whitePieceColor,
341 &appData.blackPieceColor,
342 &appData.lightSquareColor,
343 &appData.darkSquareColor,
344 &appData.highlightSquareColor,
345 &appData.premoveHighlightColor,
346 &appData.lowTimeWarningColor,
357 // [HGM] font: keep a font for each square size, even non-stndard ones
360 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
361 char *fontTable[NUM_FONTS][MAX_SIZE];
364 ParseFont (char *name, int number)
365 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
367 if(sscanf(name, "size%d:", &size)) {
368 // [HGM] font: font is meant for specific boardSize (likely from settings file);
369 // defer processing it until we know if it matches our board size
370 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
371 fontTable[number][size] = strdup(strchr(name, ':')+1);
372 fontValid[number][size] = True;
377 case 0: // CLOCK_FONT
378 appData.clockFont = strdup(name);
380 case 1: // MESSAGE_FONT
381 appData.font = strdup(name);
383 case 2: // COORD_FONT
384 appData.coordFont = strdup(name);
389 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
394 { // only 2 fonts currently
395 appData.clockFont = CLOCK_FONT_NAME;
396 appData.coordFont = COORD_FONT_NAME;
397 appData.font = DEFAULT_FONT_NAME;
402 { // no-op, until we identify the code for this already in XBoard and move it here
406 ParseColor (int n, char *name)
407 { // in XBoard, just copy the color-name string
408 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
412 ParseTextAttribs (ColorClass cc, char *s)
414 (&appData.colorShout)[cc] = strdup(s);
418 ParseBoardSize (void *addr, char *name)
420 appData.boardSize = strdup(name);
425 { // In XBoard the sound-playing program takes care of obtaining the actual sound
429 SetCommPortDefaults ()
430 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
433 // [HGM] args: these three cases taken out to stay in front-end
435 SaveFontArg (FILE *f, ArgDescriptor *ad)
438 int i, n = (int)(intptr_t)ad->argLoc;
440 case 0: // CLOCK_FONT
441 name = appData.clockFont;
443 case 1: // MESSAGE_FONT
446 case 2: // COORD_FONT
447 name = appData.coordFont;
452 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
453 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
454 fontTable[n][squareSize] = strdup(name);
455 fontValid[n][squareSize] = True;
458 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
459 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
464 { // nothing to do, as the sounds are at all times represented by their text-string names already
468 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
469 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
470 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
474 SaveColor (FILE *f, ArgDescriptor *ad)
475 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
476 if(colorVariable[(int)(intptr_t)ad->argLoc])
477 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
481 SaveBoardSize (FILE *f, char *name, void *addr)
482 { // wrapper to shield back-end from BoardSize & sizeInfo
483 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
487 ParseCommPortSettings (char *s)
488 { // no such option in XBoard (yet)
494 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
498 gtk_widget_get_allocation(shell, &a);
499 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
503 wp->height = a.height;
504 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
505 frameX = 3; frameY = 3; // remember to decide if windows touch
510 { // wrapper to shield use of window handles from back-end (make addressible by number?)
511 // In XBoard this will have to wait until awareness of window parameters is implemented
512 GetActualPlacement(shellWidget, &wpMain);
513 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
514 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
515 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
516 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
517 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
518 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
522 PrintCommPortSettings (FILE *f, char *name)
523 { // This option does not exist in XBoard
527 EnsureOnScreen (int *x, int *y, int minX, int minY)
534 { // [HGM] args: allows testing if main window is realized from back-end
535 return DialogExists(BoardWindow);
539 PopUpStartupDialog ()
540 { // start menu not implemented in XBoard
544 ConvertToLine (int argc, char **argv)
546 static char line[128*1024], buf[1024];
550 for(i=1; i<argc; i++)
552 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
553 && argv[i][0] != '{' )
554 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
556 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
557 strncat(line, buf, 128*1024 - strlen(line) - 1 );
560 line[strlen(line)-1] = NULLCHAR;
564 //--------------------------------------------------------------------------------------------
567 ResizeBoardWindow (int w, int h, int inhibit)
569 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
571 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
576 { // dummy, as the GTK code does not make colors in advance
581 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
582 { // determine what fonts to use, and create them
587 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
588 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
589 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
590 appData.font = fontTable[MESSAGE_FONT][squareSize];
591 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
592 appData.coordFont = fontTable[COORD_FONT][squareSize];
595 appData.font = InsertPxlSize(appData.font, fontPxlSize);
596 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
597 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
598 fontSet = CreateFontSet(appData.font);
599 clockFontSet = CreateFontSet(appData.clockFont);
601 /* For the coordFont, use the 0th font of the fontset. */
602 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
603 XFontStruct **font_struct_list;
604 XFontSetExtents *fontSize;
605 char **font_name_list;
606 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
607 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
608 coordFontStruct = XQueryFont(xDisplay, coordFontID);
609 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
610 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
613 appData.font = FindFont(appData.font, fontPxlSize);
614 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
615 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
616 clockFontID = XLoadFont(xDisplay, appData.clockFont);
617 clockFontStruct = XQueryFont(xDisplay, clockFontID);
618 coordFontID = XLoadFont(xDisplay, appData.coordFont);
619 coordFontStruct = XQueryFont(xDisplay, coordFontID);
620 // textHeight in !NLS mode!
622 countFontID = coordFontID; // [HGM] holdings
623 countFontStruct = coordFontStruct;
625 xdb = XtDatabase(xDisplay);
627 XrmPutLineResource(&xdb, "*international: True");
628 vTo.size = sizeof(XFontSet);
629 vTo.addr = (XtPointer) &fontSet;
630 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
632 XrmPutStringResource(&xdb, "*font", appData.font);
643 case ArgInt: p = " N"; break;
644 case ArgString: p = " STR"; break;
645 case ArgBoolean: p = " TF"; break;
646 case ArgSettingsFilename:
647 case ArgFilename: p = " FILE"; break;
648 case ArgX: p = " Nx"; break;
649 case ArgY: p = " Ny"; break;
650 case ArgAttribs: p = " TEXTCOL"; break;
651 case ArgColor: p = " COL"; break;
652 case ArgFont: p = " FONT"; break;
653 case ArgBoardSize: p = " SIZE"; break;
654 case ArgFloat: p = " FLOAT"; break;
659 case ArgCommSettings:
670 ArgDescriptor *q, *p = argDescriptors+5;
671 printf("\nXBoard accepts the following options:\n"
672 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
673 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
674 " SIZE = board-size spec(s)\n"
675 " Within parentheses are short forms, or options to set to true or false.\n"
676 " Persistent options (saved in the settings file) are marked with *)\n\n");
678 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
679 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
680 if(p->save) strcat(buf+len, "*");
681 for(q=p+1; q->argLoc == p->argLoc; q++) {
682 if(q->argName[0] == '-') continue;
683 strcat(buf+len, q == p+1 ? " (" : " ");
684 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
686 if(q != p+1) strcat(buf+len, ")");
688 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
691 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
695 SlaveResize (Option *opt)
697 static int slaveW, slaveH, w, h;
700 gtk_widget_get_allocation(shells[DummyDlg], &a);
701 w = a.width; h = a.height;
702 gtk_widget_get_allocation(opt->handle, &a);
703 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
704 slaveH = h - a.height + 13;
706 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
710 main (int argc, char **argv)
712 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
713 int boardWidth, boardHeight, w, h;
715 int forceMono = False;
717 srandom(time(0)); // [HGM] book: make random truly random
719 setbuf(stdout, NULL);
720 setbuf(stderr, NULL);
723 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
724 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
728 if(argc > 1 && !strcmp(argv[1], "--help" )) {
734 gtk_init (&argc, &argv);
736 /* set up keyboard accelerators group */
737 GtkAccelerators = gtk_accel_group_new();
739 programName = strrchr(argv[0], '/');
740 if (programName == NULL)
741 programName = argv[0];
746 // if (appData.debugMode) {
747 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
750 bindtextdomain(PACKAGE, LOCALEDIR);
751 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
755 appData.boardSize = "";
756 InitAppData(ConvertToLine(argc, argv));
758 if (p == NULL) p = "/tmp";
759 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
760 gameCopyFilename = (char*) malloc(i);
761 gamePasteFilename = (char*) malloc(i);
762 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
763 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
765 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
766 static char buf[MSG_SIZ];
767 EscapeExpand(buf, appData.firstInitString);
768 appData.firstInitString = strdup(buf);
769 EscapeExpand(buf, appData.secondInitString);
770 appData.secondInitString = strdup(buf);
771 EscapeExpand(buf, appData.firstComputerString);
772 appData.firstComputerString = strdup(buf);
773 EscapeExpand(buf, appData.secondComputerString);
774 appData.secondComputerString = strdup(buf);
777 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
780 if (chdir(chessDir) != 0) {
781 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
787 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
788 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
789 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
790 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
793 setbuf(debugFP, NULL);
797 if (appData.debugMode) {
798 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
802 /* [HGM,HR] make sure board size is acceptable */
803 if(appData.NrFiles > BOARD_FILES ||
804 appData.NrRanks > BOARD_RANKS )
805 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
808 /* This feature does not work; animation needs a rewrite */
809 appData.highlightDragging = FALSE;
813 gameInfo.variant = StringToVariant(appData.variant);
817 * determine size, based on supplied or remembered -size, or screen size
819 if (isdigit(appData.boardSize[0])) {
820 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
821 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
822 &fontPxlSize, &smallLayout, &tinyLayout);
824 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
825 programName, appData.boardSize);
829 /* Find some defaults; use the nearest known size */
830 SizeDefaults *szd, *nearest;
831 int distance = 99999;
832 nearest = szd = sizeDefaults;
833 while (szd->name != NULL) {
834 if (abs(szd->squareSize - squareSize) < distance) {
836 distance = abs(szd->squareSize - squareSize);
837 if (distance == 0) break;
841 if (i < 2) lineGap = nearest->lineGap;
842 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
843 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
844 if (i < 5) fontPxlSize = nearest->fontPxlSize;
845 if (i < 6) smallLayout = nearest->smallLayout;
846 if (i < 7) tinyLayout = nearest->tinyLayout;
849 SizeDefaults *szd = sizeDefaults;
850 if (*appData.boardSize == NULLCHAR) {
851 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
852 guint screenwidth = gdk_screen_get_width(screen);
853 guint screenheight = gdk_screen_get_height(screen);
854 while (screenwidth < szd->minScreenSize ||
855 screenheight < szd->minScreenSize) {
858 if (szd->name == NULL) szd--;
859 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
861 while (szd->name != NULL &&
862 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
863 if (szd->name == NULL) {
864 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
865 programName, appData.boardSize);
869 squareSize = szd->squareSize;
870 lineGap = szd->lineGap;
871 clockFontPxlSize = szd->clockFontPxlSize;
872 coordFontPxlSize = szd->coordFontPxlSize;
873 fontPxlSize = szd->fontPxlSize;
874 smallLayout = szd->smallLayout;
875 tinyLayout = szd->tinyLayout;
876 // [HGM] font: use defaults from settings file if available and not overruled
879 defaultLineGap = lineGap;
880 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
882 /* [HR] height treated separately (hacked) */
883 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
884 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
887 * Determine what fonts to use.
890 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
894 * Detect if there are not enough colors available and adapt.
897 if (DefaultDepth(xDisplay, xScreen) <= 2) {
898 appData.monoMode = True;
902 forceMono = MakeColors();
905 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
907 appData.monoMode = True;
910 ParseIcsTextColors();
916 layoutName = "tinyLayout";
917 } else if (smallLayout) {
918 layoutName = "smallLayout";
920 layoutName = "normalLayout";
923 optList = BoardPopUp(squareSize, lineGap, (void*)
933 InitDrawingHandle(optList + W_BOARD);
934 shellWidget = shells[BoardWindow];
935 currBoard = &optList[W_BOARD];
936 boardWidget = optList[W_BOARD].handle;
937 menuBarWidget = optList[W_MENU].handle;
938 dropMenu = optList[W_DROP].handle;
939 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
941 formWidget = XtParent(boardWidget);
942 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
943 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
944 XtGetValues(optList[W_WHITE].handle, args, 2);
945 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
946 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
947 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
948 XtGetValues(optList[W_PAUSE].handle, args, 2);
952 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
953 // not need to go into InitDrawingSizes().
957 // add accelerators to main shell
958 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
961 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
963 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
964 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
965 mainwindowIcon = WhiteIcon;
966 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
970 * Create a cursor for the board widget.
973 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
974 XChangeWindowAttributes(xDisplay, xBoardWindow,
975 CWCursor, &window_attributes);
979 * Inhibit shell resizing.
982 shellArgs[0].value = (XtArgVal) &w;
983 shellArgs[1].value = (XtArgVal) &h;
984 XtGetValues(shellWidget, shellArgs, 2);
985 shellArgs[4].value = shellArgs[2].value = w;
986 shellArgs[5].value = shellArgs[3].value = h;
987 // XtSetValues(shellWidget, &shellArgs[2], 4);
991 gtk_widget_get_allocation(shells[BoardWindow], &a);
992 w = a.width; h = a.height;
993 //printf("start size (%d,%d), %dx%d\n", a.x, a.y, w, h);
994 gtk_widget_get_allocation(boardWidget, &a);
995 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
996 marginH = h - a.height + 13;
997 gtk_window_resize(GTK_WINDOW(shellWidget), marginW + boardWidth, marginH + boardHeight);
998 //printf("margins h=%d v=%d\n", marginW, marginH);
1004 if(appData.logoSize)
1005 { // locate and read user logo
1007 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1008 ASSIGN(userLogo, buf);
1011 if (appData.animate || appData.animateDragging)
1014 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1015 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1017 /* [AS] Restore layout */
1018 if( wpMoveHistory.visible ) {
1022 if( wpEvalGraph.visible )
1027 if( wpEngineOutput.visible ) {
1028 EngineOutputPopUp();
1033 if (errorExitStatus == -1) {
1034 if (appData.icsActive) {
1035 /* We now wait until we see "login:" from the ICS before
1036 sending the logon script (problems with timestamp otherwise) */
1037 /*ICSInitScript();*/
1038 if (appData.icsInputBox) ICSInputBoxPopUp();
1042 signal(SIGWINCH, TermSizeSigHandler);
1044 signal(SIGINT, IntSigHandler);
1045 signal(SIGTERM, IntSigHandler);
1046 if (*appData.cmailGameName != NULLCHAR) {
1047 signal(SIGUSR1, CmailSigHandler);
1051 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1054 // XtSetKeyboardFocus(shellWidget, formWidget);
1056 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1059 /* check for GTK events and process them */
1062 gtk_main_iteration();
1065 if (appData.debugMode) fclose(debugFP); // [DM] debug
1070 TermSizeSigHandler (int sig)
1076 IntSigHandler (int sig)
1082 CmailSigHandler (int sig)
1087 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1089 /* Activate call-back function CmailSigHandlerCallBack() */
1090 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1092 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1096 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1099 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1101 /**** end signal code ****/
1104 #define Abs(n) ((n)<0 ? -(n) : (n))
1108 InsertPxlSize (char *pattern, int targetPxlSize)
1110 char *base_fnt_lst, strInt[12], *p, *q;
1111 int alternatives, i, len, strIntLen;
1114 * Replace the "*" (if present) in the pixel-size slot of each
1115 * alternative with the targetPxlSize.
1119 while ((p = strchr(p, ',')) != NULL) {
1123 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1124 strIntLen = strlen(strInt);
1125 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1129 while (alternatives--) {
1130 char *comma = strchr(p, ',');
1131 for (i=0; i<14; i++) {
1132 char *hyphen = strchr(p, '-');
1134 if (comma && hyphen > comma) break;
1135 len = hyphen + 1 - p;
1136 if (i == 7 && *p == '*' && len == 2) {
1138 memcpy(q, strInt, strIntLen);
1148 len = comma + 1 - p;
1155 return base_fnt_lst;
1160 CreateFontSet (char *base_fnt_lst)
1163 char **missing_list;
1167 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1168 &missing_list, &missing_count, &def_string);
1169 if (appData.debugMode) {
1171 XFontStruct **font_struct_list;
1172 char **font_name_list;
1173 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1175 fprintf(debugFP, " got list %s, locale %s\n",
1176 XBaseFontNameListOfFontSet(fntSet),
1177 XLocaleOfFontSet(fntSet));
1178 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1179 for (i = 0; i < count; i++) {
1180 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1183 for (i = 0; i < missing_count; i++) {
1184 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1187 if (fntSet == NULL) {
1188 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1194 #else // not ENABLE_NLS
1196 * Find a font that matches "pattern" that is as close as
1197 * possible to the targetPxlSize. Prefer fonts that are k
1198 * pixels smaller to fonts that are k pixels larger. The
1199 * pattern must be in the X Consortium standard format,
1200 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1201 * The return value should be freed with XtFree when no
1205 FindFont (char *pattern, int targetPxlSize)
1207 char **fonts, *p, *best, *scalable, *scalableTail;
1208 int i, j, nfonts, minerr, err, pxlSize;
1211 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1213 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1214 programName, pattern);
1221 for (i=0; i<nfonts; i++) {
1224 if (*p != '-') continue;
1226 if (*p == NULLCHAR) break;
1227 if (*p++ == '-') j++;
1229 if (j < 7) continue;
1232 scalable = fonts[i];
1235 err = pxlSize - targetPxlSize;
1236 if (Abs(err) < Abs(minerr) ||
1237 (minerr > 0 && err < 0 && -err == minerr)) {
1243 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1244 /* If the error is too big and there is a scalable font,
1245 use the scalable font. */
1246 int headlen = scalableTail - scalable;
1247 p = (char *) XtMalloc(strlen(scalable) + 10);
1248 while (isdigit(*scalableTail)) scalableTail++;
1249 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1251 p = (char *) XtMalloc(strlen(best) + 2);
1252 safeStrCpy(p, best, strlen(best)+1 );
1254 if (appData.debugMode) {
1255 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1256 pattern, targetPxlSize, p);
1258 XFreeFontNames(fonts);
1265 EnableNamedMenuItem (char *menuRef, int state)
1267 MenuItem *item = MenuNameToItem(menuRef);
1269 if(item) gtk_widget_set_sensitive(item->handle, state);
1273 EnableButtonBar (int state)
1276 XtSetSensitive(optList[W_BUTTON].handle, state);
1282 SetMenuEnables (Enables *enab)
1284 while (enab->name != NULL) {
1285 EnableNamedMenuItem(enab->name, enab->value);
1290 gboolean KeyPressProc(window, eventkey, data)
1292 GdkEventKey *eventkey;
1296 MoveTypeInProc(eventkey); // pop up for typed in moves
1299 /* check for other key values */
1300 switch(eventkey->keyval) {
1312 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1313 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1315 if(*nprms == 0) return;
1316 item = MenuNameToItem(prms[0]);
1317 if(item) ((MenuProc *) item->proc) ();
1331 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1332 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1333 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1334 dmEnables[i].piece);
1335 XtSetSensitive(entry, p != NULL || !appData.testLegality
1336 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1337 && !appData.icsActive));
1339 while (p && *p++ == dmEnables[i].piece) count++;
1340 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1342 XtSetArg(args[j], XtNlabel, label); j++;
1343 XtSetValues(entry, args, j);
1349 do_flash_delay (unsigned long msec)
1355 FlashDelay (int flash_delay)
1357 if(flash_delay) do_flash_delay(flash_delay);
1361 Fraction (int x, int start, int stop)
1363 double f = ((double) x - start)/(stop - start);
1364 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1368 static WindowPlacement wpNew;
1371 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1373 int touch=0, fudge = 2, f = 2;
1374 GetActualPlacement(sh, wp);
1375 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1376 if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x) < fudge) touch = 2; else // left touch
1377 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1378 if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y) < fudge) touch = 4; // top touch
1379 //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);
1380 if(!touch ) return; // only windows that touch co-move
1381 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1382 int heightInc = wpNew.height - wpMain.height;
1383 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1384 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1385 wp->y += fracTop * heightInc;
1386 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1388 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1390 wp->height += heightInc;
1391 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1392 int widthInc = wpNew.width - wpMain.width;
1393 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1394 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1395 wp->y += fracLeft * widthInc;
1396 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1398 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1400 wp->width += widthInc;
1402 wp->x += wpNew.x - wpMain.x;
1403 wp->y += wpNew.y - wpMain.y;
1404 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1405 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1407 XtSetArg(args[j], XtNx, wp->x); j++;
1408 XtSetArg(args[j], XtNy, wp->y); j++;
1409 XtSetValues(sh, args, j);
1411 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1412 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1413 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1417 ReSize (WindowPlacement *wp)
1419 int sqx, sqy, w, h, lg = lineGap;
1420 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1421 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1422 sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1423 if(sqy < sqx) sqx = sqy;
1424 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1425 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1426 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1427 sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1428 if(sqy < sqx) sqx = sqy;
1430 if(sqx != squareSize) {
1431 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1432 squareSize = sqx; // adopt new square size
1433 CreatePNGPieces(); // make newly scaled pieces
1434 InitDrawingSizes(0, 0); // creates grid etc.
1435 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1436 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1437 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1438 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1439 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1442 static guint delayedDragTag = 0;
1451 // GetActualPlacement(shellWidget, &wpNew);
1452 //printf("drag proc (%d,%d) %dx%d\n", wpNew.x, wpNew.y, wpNew.width, wpNew.height);
1453 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1454 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1455 busy = 0; return; // false alarm
1458 if(appData.useStickyWindows) {
1459 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1460 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1461 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1462 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1465 DrawPosition(True, NULL);
1466 if(delayedDragTag) g_source_remove(delayedDragTag);
1467 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1474 //printf("old timr = %d\n", delayedDragTag);
1475 if(delayedDragTag) g_source_remove(delayedDragTag);
1476 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1477 //printf("new timr = %d\n", delayedDragTag);
1481 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1483 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1485 wpNew.x = event->configure.x;
1486 wpNew.y = event->configure.y;
1487 wpNew.width = event->configure.width;
1488 wpNew.height = event->configure.height;
1489 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1495 /* Disable all user input other than deleting the window */
1496 static int frozen = 0;
1502 /* Grab by a widget that doesn't accept input */
1503 gtk_grab_add(optList[W_MESSG].handle);
1507 /* Undo a FreezeUI */
1511 if (!frozen) return;
1512 gtk_grab_remove(optList[W_MESSG].handle);
1519 static int oldPausing = FALSE;
1520 static GameMode oldmode = (GameMode) -1;
1522 if (!boardWidget) return;
1524 if (pausing != oldPausing) {
1525 oldPausing = pausing;
1526 MarkMenuItem("Mode.Pause", pausing);
1528 if (appData.showButtonBar) {
1529 /* Always toggle, don't set. Previous code messes up when
1530 invoked while the button is pressed, as releasing it
1531 toggles the state again. */
1533 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1534 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1538 wname = ModeToWidgetName(oldmode);
1539 if (wname != NULL) {
1540 MarkMenuItem(wname, False);
1542 wname = ModeToWidgetName(gameMode);
1543 if (wname != NULL) {
1544 MarkMenuItem(wname, True);
1547 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1549 /* Maybe all the enables should be handled here, not just this one */
1550 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1552 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1557 * Button/menu procedures
1560 void CopyFileToClipboard(gchar *filename)
1562 gchar *selection_tmp;
1566 FILE* f = fopen(filename, "r");
1569 if (f == NULL) return;
1573 selection_tmp = g_try_malloc(len + 1);
1574 if (selection_tmp == NULL) {
1575 printf("Malloc failed in CopyFileToClipboard\n");
1578 count = fread(selection_tmp, 1, len, f);
1581 g_free(selection_tmp);
1584 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1586 // copy selection_tmp to clipboard
1587 GdkDisplay *gdisp = gdk_display_get_default();
1589 g_free(selection_tmp);
1592 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1593 gtk_clipboard_set_text(cb, selection_tmp, -1);
1594 g_free(selection_tmp);
1598 CopySomething (char *src)
1600 GdkDisplay *gdisp = gdk_display_get_default();
1602 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1603 if (gdisp == NULL) return;
1604 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1605 gtk_clipboard_set_text(cb, src, -1);
1609 PastePositionProc ()
1611 GdkDisplay *gdisp = gdk_display_get_default();
1615 if (gdisp == NULL) return;
1616 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1617 fenstr = gtk_clipboard_wait_for_text(cb);
1618 if (fenstr==NULL) return; // nothing had been selected to copy
1619 EditPositionPasteFEN(fenstr);
1631 // get game from clipboard
1632 GdkDisplay *gdisp = gdk_display_get_default();
1633 if (gdisp == NULL) return;
1634 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1635 text = gtk_clipboard_wait_for_text(cb);
1636 if (text == NULL) return; // nothing to paste
1639 // write to temp file
1640 if (text == NULL || len == 0) {
1641 return; //nothing to paste
1643 f = fopen(gamePasteFilename, "w");
1645 DisplayError(_("Can't open temp file"), errno);
1648 fwrite(text, 1, len, f);
1652 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1659 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1665 void MoveTypeInProc(eventkey)
1666 GdkEventKey *eventkey;
1670 // ingnore if ctrl or alt is pressed
1671 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
1675 buf[0]=eventkey->keyval;
1683 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1685 if (!TempBackwardActive) {
1686 TempBackwardActive = True;
1692 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1694 /* Check to see if triggered by a key release event for a repeating key.
1695 * If so the next queued event will be a key press of the same key at the same time */
1696 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1698 XPeekEvent(xDisplay, &next);
1699 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1700 next.xkey.keycode == event->xkey.keycode)
1704 TempBackwardActive = False;
1708 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1709 { // called as key binding
1712 if (nprms && *nprms > 0)
1716 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
1723 { // called from menu
1725 ManInner(NULL, NULL, NULL, NULL);
1730 SetWindowTitle (char *text, char *title, char *icon)
1735 if (appData.titleInWindow) {
1737 XtSetArg(args[i], XtNlabel, text); i++;
1738 XtSetValues(titleWidget, args, i);
1741 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1742 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1743 XtSetValues(shellWidget, args, i);
1744 XSync(xDisplay, False);
1746 if (appData.titleInWindow) {
1747 SetWidgetLabel(titleWidget, text);
1749 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1754 DisplayIcsInteractionTitle (String message)
1757 if (oldICSInteractionTitle == NULL) {
1758 /* Magic to find the old window title, adapted from vim */
1759 char *wina = getenv("WINDOWID");
1761 Window win = (Window) atoi(wina);
1762 Window root, parent, *children;
1763 unsigned int nchildren;
1764 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1766 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1767 if (!XQueryTree(xDisplay, win, &root, &parent,
1768 &children, &nchildren)) break;
1769 if (children) XFree((void *)children);
1770 if (parent == root || parent == 0) break;
1773 XSetErrorHandler(oldHandler);
1775 if (oldICSInteractionTitle == NULL) {
1776 oldICSInteractionTitle = "xterm";
1779 printf("\033]0;%s\007", message);
1786 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1788 GtkWidget *w = (GtkWidget *) opt->handle;
1795 strcpy(bgcolor, "black");
1796 strcpy(fgcolor, "white");
1798 strcpy(bgcolor, "white");
1799 strcpy(fgcolor, "black");
1802 appData.lowTimeWarning &&
1803 (timer / 1000) < appData.icsAlarmTime) {
1804 strcpy(fgcolor, appData.lowTimeWarningColor);
1807 gdk_color_parse( bgcolor, &col );
1808 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1810 if (appData.clockMode) {
1811 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1812 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1814 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
1815 bgcolor, fgcolor, color);
1817 gtk_label_set_markup(GTK_LABEL(w), markup);
1821 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1824 SetClockIcon (int color)
1826 GdkPixbuf *pm = *clockIcons[color];
1827 if (mainwindowIcon != pm) {
1828 mainwindowIcon = pm;
1829 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1833 #define INPUT_SOURCE_BUF_SIZE 8192
1842 char buf[INPUT_SOURCE_BUF_SIZE];
1847 DoInputCallback(io, cond, data)
1852 /* read input from one of the input source (for example a chess program, ICS, etc).
1853 * and call a function that will handle the input
1860 /* All information (callback function, file descriptor, etc) is
1861 * saved in an InputSource structure
1863 InputSource *is = (InputSource *) data;
1865 if (is->lineByLine) {
1866 count = read(is->fd, is->unused,
1867 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1869 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1872 is->unused += count;
1874 /* break input into lines and call the callback function on each
1877 while (p < is->unused) {
1878 q = memchr(p, '\n', is->unused - p);
1879 if (q == NULL) break;
1881 (is->func)(is, is->closure, p, q - p, 0);
1884 /* remember not yet used part of the buffer */
1886 while (p < is->unused) {
1891 /* read maximum length of input buffer and send the whole buffer
1892 * to the callback function
1894 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1899 (is->func)(is, is->closure, is->buf, count, error);
1901 return True; // Must return true or the watch will be removed
1904 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1911 GIOChannel *channel;
1912 ChildProc *cp = (ChildProc *) pr;
1914 is = (InputSource *) calloc(1, sizeof(InputSource));
1915 is->lineByLine = lineByLine;
1919 is->fd = fileno(stdin);
1921 is->kind = cp->kind;
1922 is->fd = cp->fdFrom;
1925 is->unused = is->buf;
1929 /* GTK-TODO: will this work on windows?*/
1931 channel = g_io_channel_unix_new(is->fd);
1932 g_io_channel_set_close_on_unref (channel, TRUE);
1933 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1935 is->closure = closure;
1936 return (InputSourceRef) is;
1941 RemoveInputSource(isr)
1944 InputSource *is = (InputSource *) isr;
1946 if (is->sid == 0) return;
1947 g_source_remove(is->sid);
1954 static Boolean frameWaiting;
1957 FrameAlarm (int sig)
1959 frameWaiting = False;
1960 /* In case System-V style signals. Needed?? */
1961 signal(SIGALRM, FrameAlarm);
1965 FrameDelay (int time)
1967 struct itimerval delay;
1970 frameWaiting = True;
1971 signal(SIGALRM, FrameAlarm);
1972 delay.it_interval.tv_sec =
1973 delay.it_value.tv_sec = time / 1000;
1974 delay.it_interval.tv_usec =
1975 delay.it_value.tv_usec = (time % 1000) * 1000;
1976 setitimer(ITIMER_REAL, &delay, NULL);
1977 while (frameWaiting) pause();
1978 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
1979 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
1980 setitimer(ITIMER_REAL, &delay, NULL);
1987 FrameDelay (int time)
1990 XSync(xDisplay, False);
1992 // gtk_main_iteration_do(False);
1995 usleep(time * 1000);
2001 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2003 char buf[MSG_SIZ], *logoName = buf;
2004 if(appData.logo[n][0]) {
2005 logoName = appData.logo[n];
2006 } else if(appData.autoLogo) {
2007 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2008 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2009 } else if(appData.directory[n] && appData.directory[n][0]) {
2010 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2014 { ASSIGN(cps->programLogo, logoName); }
2018 UpdateLogos (int displ)
2020 if(optList[W_WHITE-1].handle == NULL) return;
2021 LoadLogo(&first, 0, 0);
2022 LoadLogo(&second, 1, appData.icsActive);
2023 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2027 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2038 GtkFileFilter *gtkfilter;
2039 GtkFileFilter *gtkfilter_all;
2041 char fileext[10] = "";
2042 char *result = NULL;
2045 /* make a copy of the filter string, so that strtok can work with it*/
2046 cp = strndup(filter,strlen(filter));
2048 /* add filters for file extensions */
2049 gtkfilter = gtk_file_filter_new();
2050 gtkfilter_all = gtk_file_filter_new();
2052 /* one filter to show everything */
2053 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2054 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2056 /* add filter if present */
2057 result = strtok(cp, space);
2058 while( result != NULL ) {
2059 snprintf(fileext,10,"*%s",result);
2060 result = strtok( NULL, space );
2061 gtk_file_filter_add_pattern(gtkfilter, fileext);
2064 /* second filter to only show what's useful */
2065 gtk_file_filter_set_name (gtkfilter,filter);
2067 if (openMode[0] == 'r')
2069 dialog = gtk_file_chooser_dialog_new (label,
2071 GTK_FILE_CHOOSER_ACTION_OPEN,
2072 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2073 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2078 dialog = gtk_file_chooser_dialog_new (label,
2080 GTK_FILE_CHOOSER_ACTION_SAVE,
2081 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2082 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2084 /* add filename suggestions */
2085 if (strlen(def) > 0 )
2086 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2088 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2092 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2093 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2094 /* activate filter */
2095 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2097 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2102 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2105 f = fopen(filename, openMode);
2108 DisplayError(_("Failed to open file"), errno);
2112 /* TODO add indec */
2114 ASSIGN(*name, filename);
2115 ScheduleDelayedEvent(DelayedLoad, 50);
2120 gtk_widget_destroy (dialog);