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 //--------------------------------------------------------------------------------------------
569 ResizeBoardWindow (int w, int h, int inhibit)
572 if(clockKludge) return; // ignore as long as clock does not have final height
573 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
574 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
575 h += marginH + a.height + 1;
576 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
581 { // dummy, as the GTK code does not make colors in advance
586 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
587 { // determine what fonts to use, and create them
592 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
593 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
594 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
595 appData.font = fontTable[MESSAGE_FONT][squareSize];
596 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
597 appData.coordFont = fontTable[COORD_FONT][squareSize];
600 appData.font = InsertPxlSize(appData.font, fontPxlSize);
601 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
602 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
603 fontSet = CreateFontSet(appData.font);
604 clockFontSet = CreateFontSet(appData.clockFont);
606 /* For the coordFont, use the 0th font of the fontset. */
607 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
608 XFontStruct **font_struct_list;
609 XFontSetExtents *fontSize;
610 char **font_name_list;
611 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
612 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
613 coordFontStruct = XQueryFont(xDisplay, coordFontID);
614 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
615 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
618 appData.font = FindFont(appData.font, fontPxlSize);
619 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
620 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
621 clockFontID = XLoadFont(xDisplay, appData.clockFont);
622 clockFontStruct = XQueryFont(xDisplay, clockFontID);
623 coordFontID = XLoadFont(xDisplay, appData.coordFont);
624 coordFontStruct = XQueryFont(xDisplay, coordFontID);
625 // textHeight in !NLS mode!
627 countFontID = coordFontID; // [HGM] holdings
628 countFontStruct = coordFontStruct;
630 xdb = XtDatabase(xDisplay);
632 XrmPutLineResource(&xdb, "*international: True");
633 vTo.size = sizeof(XFontSet);
634 vTo.addr = (XtPointer) &fontSet;
635 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
637 XrmPutStringResource(&xdb, "*font", appData.font);
648 case ArgInt: p = " N"; break;
649 case ArgString: p = " STR"; break;
650 case ArgBoolean: p = " TF"; break;
651 case ArgSettingsFilename:
652 case ArgBackupSettingsFile:
653 case ArgFilename: p = " FILE"; break;
654 case ArgX: p = " Nx"; break;
655 case ArgY: p = " Ny"; break;
656 case ArgAttribs: p = " TEXTCOL"; break;
657 case ArgColor: p = " COL"; break;
658 case ArgFont: p = " FONT"; break;
659 case ArgBoardSize: p = " SIZE"; break;
660 case ArgFloat: p = " FLOAT"; break;
665 case ArgCommSettings:
676 ArgDescriptor *q, *p = argDescriptors+5;
677 printf("\nXBoard accepts the following options:\n"
678 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
679 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
680 " SIZE = board-size spec(s)\n"
681 " Within parentheses are short forms, or options to set to true or false.\n"
682 " Persistent options (saved in the settings file) are marked with *)\n\n");
684 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
685 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
686 if(p->save) strcat(buf+len, "*");
687 for(q=p+1; q->argLoc == p->argLoc; q++) {
688 if(q->argName[0] == '-') continue;
689 strcat(buf+len, q == p+1 ? " (" : " ");
690 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
692 if(q != p+1) strcat(buf+len, ")");
694 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
697 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
701 SlaveResize (Option *opt)
703 static int slaveW, slaveH, w, h;
706 gtk_widget_get_allocation(shells[DummyDlg], &a);
707 w = a.width; h = a.height;
708 gtk_widget_get_allocation(opt->handle, &a);
709 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
710 slaveH = h - a.height + 13;
712 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
716 main (int argc, char **argv)
718 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
719 int boardWidth, boardHeight, w, h;
721 int forceMono = False;
723 srandom(time(0)); // [HGM] book: make random truly random
725 setbuf(stdout, NULL);
726 setbuf(stderr, NULL);
729 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
730 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
734 if(argc > 1 && !strcmp(argv[1], "--help" )) {
740 gtk_init (&argc, &argv);
742 /* set up keyboard accelerators group */
743 GtkAccelerators = gtk_accel_group_new();
745 programName = strrchr(argv[0], '/');
746 if (programName == NULL)
747 programName = argv[0];
752 // if (appData.debugMode) {
753 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
756 bindtextdomain(PACKAGE, LOCALEDIR);
757 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
761 appData.boardSize = "";
762 InitAppData(ConvertToLine(argc, argv));
764 if (p == NULL) p = "/tmp";
765 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
766 gameCopyFilename = (char*) malloc(i);
767 gamePasteFilename = (char*) malloc(i);
768 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
769 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
771 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
772 static char buf[MSG_SIZ];
773 EscapeExpand(buf, appData.firstInitString);
774 appData.firstInitString = strdup(buf);
775 EscapeExpand(buf, appData.secondInitString);
776 appData.secondInitString = strdup(buf);
777 EscapeExpand(buf, appData.firstComputerString);
778 appData.firstComputerString = strdup(buf);
779 EscapeExpand(buf, appData.secondComputerString);
780 appData.secondComputerString = strdup(buf);
783 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
786 if (chdir(chessDir) != 0) {
787 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
793 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
794 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
795 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
796 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
799 setbuf(debugFP, NULL);
803 if (appData.debugMode) {
804 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
808 /* [HGM,HR] make sure board size is acceptable */
809 if(appData.NrFiles > BOARD_FILES ||
810 appData.NrRanks > BOARD_RANKS )
811 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
814 /* This feature does not work; animation needs a rewrite */
815 appData.highlightDragging = FALSE;
819 gameInfo.variant = StringToVariant(appData.variant);
823 * determine size, based on supplied or remembered -size, or screen size
825 if (isdigit(appData.boardSize[0])) {
826 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
827 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
828 &fontPxlSize, &smallLayout, &tinyLayout);
830 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
831 programName, appData.boardSize);
835 /* Find some defaults; use the nearest known size */
836 SizeDefaults *szd, *nearest;
837 int distance = 99999;
838 nearest = szd = sizeDefaults;
839 while (szd->name != NULL) {
840 if (abs(szd->squareSize - squareSize) < distance) {
842 distance = abs(szd->squareSize - squareSize);
843 if (distance == 0) break;
847 if (i < 2) lineGap = nearest->lineGap;
848 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
849 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
850 if (i < 5) fontPxlSize = nearest->fontPxlSize;
851 if (i < 6) smallLayout = nearest->smallLayout;
852 if (i < 7) tinyLayout = nearest->tinyLayout;
855 SizeDefaults *szd = sizeDefaults;
856 if (*appData.boardSize == NULLCHAR) {
857 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
858 guint screenwidth = gdk_screen_get_width(screen);
859 guint screenheight = gdk_screen_get_height(screen);
860 while (screenwidth < szd->minScreenSize ||
861 screenheight < szd->minScreenSize) {
864 if (szd->name == NULL) szd--;
865 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
867 while (szd->name != NULL &&
868 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
869 if (szd->name == NULL) {
870 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
871 programName, appData.boardSize);
875 squareSize = szd->squareSize;
876 lineGap = szd->lineGap;
877 clockFontPxlSize = szd->clockFontPxlSize;
878 coordFontPxlSize = szd->coordFontPxlSize;
879 fontPxlSize = szd->fontPxlSize;
880 smallLayout = szd->smallLayout;
881 tinyLayout = szd->tinyLayout;
882 // [HGM] font: use defaults from settings file if available and not overruled
885 defaultLineGap = lineGap;
886 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
888 /* [HR] height treated separately (hacked) */
889 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
890 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
893 * Determine what fonts to use.
896 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
900 * Detect if there are not enough colors available and adapt.
903 if (DefaultDepth(xDisplay, xScreen) <= 2) {
904 appData.monoMode = True;
908 forceMono = MakeColors();
911 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
913 appData.monoMode = True;
916 ParseIcsTextColors();
922 layoutName = "tinyLayout";
923 } else if (smallLayout) {
924 layoutName = "smallLayout";
926 layoutName = "normalLayout";
929 wpMain.width = -1; // prevent popup sizes window
930 optList = BoardPopUp(squareSize, lineGap, (void*)
940 InitDrawingHandle(optList + W_BOARD);
941 shellWidget = shells[BoardWindow];
942 currBoard = &optList[W_BOARD];
943 boardWidget = optList[W_BOARD].handle;
944 menuBarWidget = optList[W_MENU].handle;
945 dropMenu = optList[W_DROP].handle;
946 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
948 formWidget = XtParent(boardWidget);
949 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
950 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
951 XtGetValues(optList[W_WHITE].handle, args, 2);
952 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
953 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
954 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
955 XtGetValues(optList[W_PAUSE].handle, args, 2);
959 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
960 // not need to go into InitDrawingSizes().
964 // add accelerators to main shell
965 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
968 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
970 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
971 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
972 mainwindowIcon = WhiteIcon;
973 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
977 * Create a cursor for the board widget.
980 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
981 XChangeWindowAttributes(xDisplay, xBoardWindow,
982 CWCursor, &window_attributes);
986 * Inhibit shell resizing.
989 shellArgs[0].value = (XtArgVal) &w;
990 shellArgs[1].value = (XtArgVal) &h;
991 XtGetValues(shellWidget, shellArgs, 2);
992 shellArgs[4].value = shellArgs[2].value = w;
993 shellArgs[5].value = shellArgs[3].value = h;
994 // XtSetValues(shellWidget, &shellArgs[2], 4);
997 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
998 // It wil only become known asynchronously, when we first write a string into it.
999 // This will then change the clock widget height, which triggers resizing the top-level window
1000 // and a configure event. Only then can we know the total height of the top-level window,
1001 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1002 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1005 gtk_widget_get_allocation(shells[BoardWindow], &a);
1006 w = a.width; h = a.height;
1007 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1008 clockKludge = hc = a.height;
1009 gtk_widget_get_allocation(boardWidget, &a);
1010 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1011 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1017 if(appData.logoSize)
1018 { // locate and read user logo
1020 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1021 ASSIGN(userLogo, buf);
1024 if (appData.animate || appData.animateDragging)
1027 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1028 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1030 /* [AS] Restore layout */
1031 if( wpMoveHistory.visible ) {
1035 if( wpEvalGraph.visible )
1040 if( wpEngineOutput.visible ) {
1041 EngineOutputPopUp();
1046 if (errorExitStatus == -1) {
1047 if (appData.icsActive) {
1048 /* We now wait until we see "login:" from the ICS before
1049 sending the logon script (problems with timestamp otherwise) */
1050 /*ICSInitScript();*/
1051 if (appData.icsInputBox) ICSInputBoxPopUp();
1055 signal(SIGWINCH, TermSizeSigHandler);
1057 signal(SIGINT, IntSigHandler);
1058 signal(SIGTERM, IntSigHandler);
1059 if (*appData.cmailGameName != NULLCHAR) {
1060 signal(SIGUSR1, CmailSigHandler);
1064 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1067 // XtSetKeyboardFocus(shellWidget, formWidget);
1069 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1072 /* check for GTK events and process them */
1075 gtk_main_iteration();
1078 if (appData.debugMode) fclose(debugFP); // [DM] debug
1083 TermSizeSigHandler (int sig)
1089 IntSigHandler (int sig)
1095 CmailSigHandler (int sig)
1100 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1102 /* Activate call-back function CmailSigHandlerCallBack() */
1103 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1105 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1109 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1112 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1114 /**** end signal code ****/
1117 #define Abs(n) ((n)<0 ? -(n) : (n))
1121 InsertPxlSize (char *pattern, int targetPxlSize)
1123 char *base_fnt_lst, strInt[12], *p, *q;
1124 int alternatives, i, len, strIntLen;
1127 * Replace the "*" (if present) in the pixel-size slot of each
1128 * alternative with the targetPxlSize.
1132 while ((p = strchr(p, ',')) != NULL) {
1136 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1137 strIntLen = strlen(strInt);
1138 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1142 while (alternatives--) {
1143 char *comma = strchr(p, ',');
1144 for (i=0; i<14; i++) {
1145 char *hyphen = strchr(p, '-');
1147 if (comma && hyphen > comma) break;
1148 len = hyphen + 1 - p;
1149 if (i == 7 && *p == '*' && len == 2) {
1151 memcpy(q, strInt, strIntLen);
1161 len = comma + 1 - p;
1168 return base_fnt_lst;
1173 CreateFontSet (char *base_fnt_lst)
1176 char **missing_list;
1180 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1181 &missing_list, &missing_count, &def_string);
1182 if (appData.debugMode) {
1184 XFontStruct **font_struct_list;
1185 char **font_name_list;
1186 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1188 fprintf(debugFP, " got list %s, locale %s\n",
1189 XBaseFontNameListOfFontSet(fntSet),
1190 XLocaleOfFontSet(fntSet));
1191 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1192 for (i = 0; i < count; i++) {
1193 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1196 for (i = 0; i < missing_count; i++) {
1197 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1200 if (fntSet == NULL) {
1201 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1207 #else // not ENABLE_NLS
1209 * Find a font that matches "pattern" that is as close as
1210 * possible to the targetPxlSize. Prefer fonts that are k
1211 * pixels smaller to fonts that are k pixels larger. The
1212 * pattern must be in the X Consortium standard format,
1213 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1214 * The return value should be freed with XtFree when no
1218 FindFont (char *pattern, int targetPxlSize)
1220 char **fonts, *p, *best, *scalable, *scalableTail;
1221 int i, j, nfonts, minerr, err, pxlSize;
1224 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1226 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1227 programName, pattern);
1234 for (i=0; i<nfonts; i++) {
1237 if (*p != '-') continue;
1239 if (*p == NULLCHAR) break;
1240 if (*p++ == '-') j++;
1242 if (j < 7) continue;
1245 scalable = fonts[i];
1248 err = pxlSize - targetPxlSize;
1249 if (Abs(err) < Abs(minerr) ||
1250 (minerr > 0 && err < 0 && -err == minerr)) {
1256 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1257 /* If the error is too big and there is a scalable font,
1258 use the scalable font. */
1259 int headlen = scalableTail - scalable;
1260 p = (char *) XtMalloc(strlen(scalable) + 10);
1261 while (isdigit(*scalableTail)) scalableTail++;
1262 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1264 p = (char *) XtMalloc(strlen(best) + 2);
1265 safeStrCpy(p, best, strlen(best)+1 );
1267 if (appData.debugMode) {
1268 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1269 pattern, targetPxlSize, p);
1271 XFreeFontNames(fonts);
1278 EnableNamedMenuItem (char *menuRef, int state)
1280 MenuItem *item = MenuNameToItem(menuRef);
1282 if(item) gtk_widget_set_sensitive(item->handle, state);
1286 EnableButtonBar (int state)
1289 XtSetSensitive(optList[W_BUTTON].handle, state);
1295 SetMenuEnables (Enables *enab)
1297 while (enab->name != NULL) {
1298 EnableNamedMenuItem(enab->name, enab->value);
1303 gboolean KeyPressProc(window, eventkey, data)
1305 GdkEventKey *eventkey;
1309 MoveTypeInProc(eventkey); // pop up for typed in moves
1312 /* check for other key values */
1313 switch(eventkey->keyval) {
1325 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1326 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1328 if(*nprms == 0) return;
1329 item = MenuNameToItem(prms[0]);
1330 if(item) ((MenuProc *) item->proc) ();
1344 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1345 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1346 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1347 dmEnables[i].piece);
1348 XtSetSensitive(entry, p != NULL || !appData.testLegality
1349 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1350 && !appData.icsActive));
1352 while (p && *p++ == dmEnables[i].piece) count++;
1353 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1355 XtSetArg(args[j], XtNlabel, label); j++;
1356 XtSetValues(entry, args, j);
1362 do_flash_delay (unsigned long msec)
1368 FlashDelay (int flash_delay)
1370 if(flash_delay) do_flash_delay(flash_delay);
1374 Fraction (int x, int start, int stop)
1376 double f = ((double) x - start)/(stop - start);
1377 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1381 static WindowPlacement wpNew;
1384 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1386 int touch=0, fudge = 2, f = 2;
1387 GetActualPlacement(sh, wp);
1388 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1389 if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x) < fudge) touch = 2; else // left touch
1390 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1391 if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y) < fudge) touch = 4; // top touch
1392 //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);
1393 if(!touch ) return; // only windows that touch co-move
1394 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1395 int heightInc = wpNew.height - wpMain.height;
1396 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1397 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1398 wp->y += fracTop * heightInc;
1399 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1401 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1403 wp->height += heightInc;
1404 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1405 int widthInc = wpNew.width - wpMain.width;
1406 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1407 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1408 wp->y += fracLeft * widthInc;
1409 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1411 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1413 wp->width += widthInc;
1415 wp->x += wpNew.x - wpMain.x;
1416 wp->y += wpNew.y - wpMain.y;
1417 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1418 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1420 XtSetArg(args[j], XtNx, wp->x); j++;
1421 XtSetArg(args[j], XtNy, wp->y); j++;
1422 XtSetValues(sh, args, j);
1424 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1425 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1426 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1430 ReSize (WindowPlacement *wp)
1433 int sqx, sqy, w, h, hc, lg = lineGap;
1434 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1435 hc = a.height; // clock height can depend on single / double line clock text!
1436 if(clockKludge == a.height) return; // wait for clock to get final size at startup
1437 if(clockKludge) { // clock height OK now; calculate desired initial board height
1439 wp->height = BOARD_HEIGHT * (squareSize + lineGap) + lineGap + marginH + hc;
1441 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1442 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1443 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1444 if(sqy < sqx) sqx = sqy;
1445 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1446 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1447 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1448 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1449 if(sqy < sqx) sqx = sqy;
1451 if(sqx != squareSize) {
1452 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1453 squareSize = sqx; // adopt new square size
1454 CreatePNGPieces(); // make newly scaled pieces
1455 InitDrawingSizes(0, 0); // creates grid etc.
1456 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1457 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1458 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1459 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1460 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1463 static guint delayedDragTag = 0;
1472 // GetActualPlacement(shellWidget, &wpNew);
1473 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1474 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1475 busy = 0; return; // false alarm
1478 if(appData.useStickyWindows) {
1479 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1480 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1481 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1482 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1485 DrawPosition(True, NULL);
1486 if(delayedDragTag) g_source_remove(delayedDragTag);
1487 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1494 //printf("old timr = %d\n", delayedDragTag);
1495 if(delayedDragTag) g_source_remove(delayedDragTag);
1496 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1497 //printf("new timr = %d\n", delayedDragTag);
1501 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1503 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1505 wpNew.x = event->configure.x;
1506 wpNew.y = event->configure.y;
1507 wpNew.width = event->configure.width;
1508 wpNew.height = event->configure.height;
1509 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1515 /* Disable all user input other than deleting the window */
1516 static int frozen = 0;
1522 /* Grab by a widget that doesn't accept input */
1523 gtk_grab_add(optList[W_MESSG].handle);
1527 /* Undo a FreezeUI */
1531 if (!frozen) return;
1532 gtk_grab_remove(optList[W_MESSG].handle);
1539 static int oldPausing = FALSE;
1540 static GameMode oldmode = (GameMode) -1;
1542 if (!boardWidget) return;
1544 if (pausing != oldPausing) {
1545 oldPausing = pausing;
1546 MarkMenuItem("Mode.Pause", pausing);
1548 if (appData.showButtonBar) {
1549 /* Always toggle, don't set. Previous code messes up when
1550 invoked while the button is pressed, as releasing it
1551 toggles the state again. */
1553 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1554 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1558 wname = ModeToWidgetName(oldmode);
1559 if (wname != NULL) {
1560 MarkMenuItem(wname, False);
1562 wname = ModeToWidgetName(gameMode);
1563 if (wname != NULL) {
1564 MarkMenuItem(wname, True);
1567 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1569 /* Maybe all the enables should be handled here, not just this one */
1570 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1572 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1577 * Button/menu procedures
1580 void CopyFileToClipboard(gchar *filename)
1582 gchar *selection_tmp;
1586 FILE* f = fopen(filename, "r");
1589 if (f == NULL) return;
1593 selection_tmp = g_try_malloc(len + 1);
1594 if (selection_tmp == NULL) {
1595 printf("Malloc failed in CopyFileToClipboard\n");
1598 count = fread(selection_tmp, 1, len, f);
1601 g_free(selection_tmp);
1604 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1606 // copy selection_tmp to clipboard
1607 GdkDisplay *gdisp = gdk_display_get_default();
1609 g_free(selection_tmp);
1612 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1613 gtk_clipboard_set_text(cb, selection_tmp, -1);
1614 g_free(selection_tmp);
1618 CopySomething (char *src)
1620 GdkDisplay *gdisp = gdk_display_get_default();
1622 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1623 if (gdisp == NULL) return;
1624 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1625 gtk_clipboard_set_text(cb, src, -1);
1629 PastePositionProc ()
1631 GdkDisplay *gdisp = gdk_display_get_default();
1635 if (gdisp == NULL) return;
1636 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1637 fenstr = gtk_clipboard_wait_for_text(cb);
1638 if (fenstr==NULL) return; // nothing had been selected to copy
1639 EditPositionPasteFEN(fenstr);
1651 // get game from clipboard
1652 GdkDisplay *gdisp = gdk_display_get_default();
1653 if (gdisp == NULL) return;
1654 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1655 text = gtk_clipboard_wait_for_text(cb);
1656 if (text == NULL) return; // nothing to paste
1659 // write to temp file
1660 if (text == NULL || len == 0) {
1661 return; //nothing to paste
1663 f = fopen(gamePasteFilename, "w");
1665 DisplayError(_("Can't open temp file"), errno);
1668 fwrite(text, 1, len, f);
1672 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1679 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1685 void MoveTypeInProc(eventkey)
1686 GdkEventKey *eventkey;
1690 // ingnore if ctrl or alt is pressed
1691 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
1695 buf[0]=eventkey->keyval;
1697 if (eventkey->keyval > 32 && eventkey->keyval < 256)
1703 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1705 if (!TempBackwardActive) {
1706 TempBackwardActive = True;
1712 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1714 /* Check to see if triggered by a key release event for a repeating key.
1715 * If so the next queued event will be a key press of the same key at the same time */
1716 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1718 XPeekEvent(xDisplay, &next);
1719 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1720 next.xkey.keycode == event->xkey.keycode)
1724 TempBackwardActive = False;
1728 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1729 { // called as key binding
1732 if (nprms && *nprms > 0)
1736 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
1743 { // called from menu
1745 ManInner(NULL, NULL, NULL, NULL);
1750 SetWindowTitle (char *text, char *title, char *icon)
1755 if (appData.titleInWindow) {
1757 XtSetArg(args[i], XtNlabel, text); i++;
1758 XtSetValues(titleWidget, args, i);
1761 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1762 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1763 XtSetValues(shellWidget, args, i);
1764 XSync(xDisplay, False);
1766 if (appData.titleInWindow) {
1767 SetWidgetLabel(titleWidget, text);
1769 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1774 DisplayIcsInteractionTitle (String message)
1777 if (oldICSInteractionTitle == NULL) {
1778 /* Magic to find the old window title, adapted from vim */
1779 char *wina = getenv("WINDOWID");
1781 Window win = (Window) atoi(wina);
1782 Window root, parent, *children;
1783 unsigned int nchildren;
1784 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1786 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1787 if (!XQueryTree(xDisplay, win, &root, &parent,
1788 &children, &nchildren)) break;
1789 if (children) XFree((void *)children);
1790 if (parent == root || parent == 0) break;
1793 XSetErrorHandler(oldHandler);
1795 if (oldICSInteractionTitle == NULL) {
1796 oldICSInteractionTitle = "xterm";
1799 printf("\033]0;%s\007", message);
1806 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1808 GtkWidget *w = (GtkWidget *) opt->handle;
1815 strcpy(bgcolor, "black");
1816 strcpy(fgcolor, "white");
1818 strcpy(bgcolor, "white");
1819 strcpy(fgcolor, "black");
1822 appData.lowTimeWarning &&
1823 (timer / 1000) < appData.icsAlarmTime) {
1824 strcpy(fgcolor, appData.lowTimeWarningColor);
1827 gdk_color_parse( bgcolor, &col );
1828 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1830 if (appData.clockMode) {
1831 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1832 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1834 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
1835 bgcolor, fgcolor, color);
1837 gtk_label_set_markup(GTK_LABEL(w), markup);
1841 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1844 SetClockIcon (int color)
1846 GdkPixbuf *pm = *clockIcons[color];
1847 if (mainwindowIcon != pm) {
1848 mainwindowIcon = pm;
1849 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1853 #define INPUT_SOURCE_BUF_SIZE 8192
1862 char buf[INPUT_SOURCE_BUF_SIZE];
1867 DoInputCallback(io, cond, data)
1872 /* read input from one of the input source (for example a chess program, ICS, etc).
1873 * and call a function that will handle the input
1880 /* All information (callback function, file descriptor, etc) is
1881 * saved in an InputSource structure
1883 InputSource *is = (InputSource *) data;
1885 if (is->lineByLine) {
1886 count = read(is->fd, is->unused,
1887 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1889 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1892 is->unused += count;
1894 /* break input into lines and call the callback function on each
1897 while (p < is->unused) {
1898 q = memchr(p, '\n', is->unused - p);
1899 if (q == NULL) break;
1901 (is->func)(is, is->closure, p, q - p, 0);
1904 /* remember not yet used part of the buffer */
1906 while (p < is->unused) {
1911 /* read maximum length of input buffer and send the whole buffer
1912 * to the callback function
1914 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1919 (is->func)(is, is->closure, is->buf, count, error);
1921 return True; // Must return true or the watch will be removed
1924 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1931 GIOChannel *channel;
1932 ChildProc *cp = (ChildProc *) pr;
1934 is = (InputSource *) calloc(1, sizeof(InputSource));
1935 is->lineByLine = lineByLine;
1939 is->fd = fileno(stdin);
1941 is->kind = cp->kind;
1942 is->fd = cp->fdFrom;
1945 is->unused = is->buf;
1949 /* GTK-TODO: will this work on windows?*/
1951 channel = g_io_channel_unix_new(is->fd);
1952 g_io_channel_set_close_on_unref (channel, TRUE);
1953 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1955 is->closure = closure;
1956 return (InputSourceRef) is;
1961 RemoveInputSource(isr)
1964 InputSource *is = (InputSource *) isr;
1966 if (is->sid == 0) return;
1967 g_source_remove(is->sid);
1974 static Boolean frameWaiting;
1977 FrameAlarm (int sig)
1979 frameWaiting = False;
1980 /* In case System-V style signals. Needed?? */
1981 signal(SIGALRM, FrameAlarm);
1985 FrameDelay (int time)
1987 struct itimerval delay;
1990 frameWaiting = True;
1991 signal(SIGALRM, FrameAlarm);
1992 delay.it_interval.tv_sec =
1993 delay.it_value.tv_sec = time / 1000;
1994 delay.it_interval.tv_usec =
1995 delay.it_value.tv_usec = (time % 1000) * 1000;
1996 setitimer(ITIMER_REAL, &delay, NULL);
1997 while (frameWaiting) pause();
1998 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
1999 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2000 setitimer(ITIMER_REAL, &delay, NULL);
2007 FrameDelay (int time)
2010 XSync(xDisplay, False);
2012 // gtk_main_iteration_do(False);
2015 usleep(time * 1000);
2021 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2023 char buf[MSG_SIZ], *logoName = buf;
2024 if(appData.logo[n][0]) {
2025 logoName = appData.logo[n];
2026 } else if(appData.autoLogo) {
2027 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2028 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2029 } else if(appData.directory[n] && appData.directory[n][0]) {
2030 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2034 { ASSIGN(cps->programLogo, logoName); }
2038 UpdateLogos (int displ)
2040 if(optList[W_WHITE-1].handle == NULL) return;
2041 LoadLogo(&first, 0, 0);
2042 LoadLogo(&second, 1, appData.icsActive);
2043 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2047 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2058 GtkFileFilter *gtkfilter;
2059 GtkFileFilter *gtkfilter_all;
2061 char fileext[10] = "";
2062 char *result = NULL;
2065 /* make a copy of the filter string, so that strtok can work with it*/
2066 cp = strndup(filter,strlen(filter));
2068 /* add filters for file extensions */
2069 gtkfilter = gtk_file_filter_new();
2070 gtkfilter_all = gtk_file_filter_new();
2072 /* one filter to show everything */
2073 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2074 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2076 /* add filter if present */
2077 result = strtok(cp, space);
2078 while( result != NULL ) {
2079 snprintf(fileext,10,"*%s",result);
2080 result = strtok( NULL, space );
2081 gtk_file_filter_add_pattern(gtkfilter, fileext);
2084 /* second filter to only show what's useful */
2085 gtk_file_filter_set_name (gtkfilter,filter);
2087 if (openMode[0] == 'r')
2089 dialog = gtk_file_chooser_dialog_new (label,
2091 GTK_FILE_CHOOSER_ACTION_OPEN,
2092 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2093 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2098 dialog = gtk_file_chooser_dialog_new (label,
2100 GTK_FILE_CHOOSER_ACTION_SAVE,
2101 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2102 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2104 /* add filename suggestions */
2105 if (strlen(def) > 0 )
2106 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2108 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2112 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2113 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2114 /* activate filter */
2115 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2117 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2122 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2125 f = fopen(filename, openMode);
2128 DisplayError(_("Failed to open file"), errno);
2132 /* TODO add indec */
2134 ASSIGN(*name, filename);
2135 ScheduleDelayedEvent(DelayedLoad, 50);
2140 gtk_widget_destroy (dialog);