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:
677 ArgDescriptor *q, *p = argDescriptors+5;
678 printf("\nXBoard accepts the following options:\n"
679 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
680 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
681 " SIZE = board-size spec(s)\n"
682 " Within parentheses are short forms, or options to set to true or false.\n"
683 " Persistent options (saved in the settings file) are marked with *)\n\n");
685 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
686 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
687 if(p->save) strcat(buf+len, "*");
688 for(q=p+1; q->argLoc == p->argLoc; q++) {
689 if(q->argName[0] == '-') continue;
690 strcat(buf+len, q == p+1 ? " (" : " ");
691 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
693 if(q != p+1) strcat(buf+len, ")");
695 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
698 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
702 SlaveResize (Option *opt)
704 static int slaveW, slaveH, w, h;
707 gtk_widget_get_allocation(shells[DummyDlg], &a);
708 w = a.width; h = a.height;
709 gtk_widget_get_allocation(opt->handle, &a);
710 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
711 slaveH = h - a.height + 13;
713 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
717 main (int argc, char **argv)
719 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
720 int boardWidth, boardHeight, w, h;
722 int forceMono = False;
724 srandom(time(0)); // [HGM] book: make random truly random
726 setbuf(stdout, NULL);
727 setbuf(stderr, NULL);
730 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
731 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
735 if(argc > 1 && !strcmp(argv[1], "--help" )) {
741 gtk_init (&argc, &argv);
743 /* set up keyboard accelerators group */
744 GtkAccelerators = gtk_accel_group_new();
746 programName = strrchr(argv[0], '/');
747 if (programName == NULL)
748 programName = argv[0];
753 // if (appData.debugMode) {
754 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
757 bindtextdomain(PACKAGE, LOCALEDIR);
758 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
762 appData.boardSize = "";
763 InitAppData(ConvertToLine(argc, argv));
765 if (p == NULL) p = "/tmp";
766 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
767 gameCopyFilename = (char*) malloc(i);
768 gamePasteFilename = (char*) malloc(i);
769 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
770 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
772 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
773 static char buf[MSG_SIZ];
774 EscapeExpand(buf, appData.firstInitString);
775 appData.firstInitString = strdup(buf);
776 EscapeExpand(buf, appData.secondInitString);
777 appData.secondInitString = strdup(buf);
778 EscapeExpand(buf, appData.firstComputerString);
779 appData.firstComputerString = strdup(buf);
780 EscapeExpand(buf, appData.secondComputerString);
781 appData.secondComputerString = strdup(buf);
784 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
787 if (chdir(chessDir) != 0) {
788 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
794 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
795 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
796 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
797 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
800 setbuf(debugFP, NULL);
804 if (appData.debugMode) {
805 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
809 /* [HGM,HR] make sure board size is acceptable */
810 if(appData.NrFiles > BOARD_FILES ||
811 appData.NrRanks > BOARD_RANKS )
812 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
815 /* This feature does not work; animation needs a rewrite */
816 appData.highlightDragging = FALSE;
820 gameInfo.variant = StringToVariant(appData.variant);
824 * determine size, based on supplied or remembered -size, or screen size
826 if (isdigit(appData.boardSize[0])) {
827 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
828 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
829 &fontPxlSize, &smallLayout, &tinyLayout);
831 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
832 programName, appData.boardSize);
836 /* Find some defaults; use the nearest known size */
837 SizeDefaults *szd, *nearest;
838 int distance = 99999;
839 nearest = szd = sizeDefaults;
840 while (szd->name != NULL) {
841 if (abs(szd->squareSize - squareSize) < distance) {
843 distance = abs(szd->squareSize - squareSize);
844 if (distance == 0) break;
848 if (i < 2) lineGap = nearest->lineGap;
849 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
850 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
851 if (i < 5) fontPxlSize = nearest->fontPxlSize;
852 if (i < 6) smallLayout = nearest->smallLayout;
853 if (i < 7) tinyLayout = nearest->tinyLayout;
856 SizeDefaults *szd = sizeDefaults;
857 if (*appData.boardSize == NULLCHAR) {
858 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
859 guint screenwidth = gdk_screen_get_width(screen);
860 guint screenheight = gdk_screen_get_height(screen);
861 while (screenwidth < szd->minScreenSize ||
862 screenheight < szd->minScreenSize) {
865 if (szd->name == NULL) szd--;
866 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
868 while (szd->name != NULL &&
869 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
870 if (szd->name == NULL) {
871 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
872 programName, appData.boardSize);
876 squareSize = szd->squareSize;
877 lineGap = szd->lineGap;
878 clockFontPxlSize = szd->clockFontPxlSize;
879 coordFontPxlSize = szd->coordFontPxlSize;
880 fontPxlSize = szd->fontPxlSize;
881 smallLayout = szd->smallLayout;
882 tinyLayout = szd->tinyLayout;
883 // [HGM] font: use defaults from settings file if available and not overruled
886 defaultLineGap = lineGap;
887 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
889 /* [HR] height treated separately (hacked) */
890 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
891 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
894 * Determine what fonts to use.
897 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
901 * Detect if there are not enough colors available and adapt.
904 if (DefaultDepth(xDisplay, xScreen) <= 2) {
905 appData.monoMode = True;
909 forceMono = MakeColors();
912 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
914 appData.monoMode = True;
917 ParseIcsTextColors();
923 layoutName = "tinyLayout";
924 } else if (smallLayout) {
925 layoutName = "smallLayout";
927 layoutName = "normalLayout";
930 wpMain.width = -1; // prevent popup sizes window
931 optList = BoardPopUp(squareSize, lineGap, (void*)
941 InitDrawingHandle(optList + W_BOARD);
942 shellWidget = shells[BoardWindow];
943 currBoard = &optList[W_BOARD];
944 boardWidget = optList[W_BOARD].handle;
945 menuBarWidget = optList[W_MENU].handle;
946 dropMenu = optList[W_DROP].handle;
947 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
949 formWidget = XtParent(boardWidget);
950 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
951 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
952 XtGetValues(optList[W_WHITE].handle, args, 2);
953 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
954 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
955 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
956 XtGetValues(optList[W_PAUSE].handle, args, 2);
960 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
961 // not need to go into InitDrawingSizes().
965 // add accelerators to main shell
966 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
969 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
971 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
972 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
973 mainwindowIcon = WhiteIcon;
974 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
978 * Create a cursor for the board widget.
981 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
982 XChangeWindowAttributes(xDisplay, xBoardWindow,
983 CWCursor, &window_attributes);
987 * Inhibit shell resizing.
990 shellArgs[0].value = (XtArgVal) &w;
991 shellArgs[1].value = (XtArgVal) &h;
992 XtGetValues(shellWidget, shellArgs, 2);
993 shellArgs[4].value = shellArgs[2].value = w;
994 shellArgs[5].value = shellArgs[3].value = h;
995 // XtSetValues(shellWidget, &shellArgs[2], 4);
998 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
999 // It wil only become known asynchronously, when we first write a string into it.
1000 // This will then change the clock widget height, which triggers resizing the top-level window
1001 // and a configure event. Only then can we know the total height of the top-level window,
1002 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1003 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1006 gtk_widget_get_allocation(shells[BoardWindow], &a);
1007 w = a.width; h = a.height;
1008 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1009 clockKludge = hc = a.height;
1010 gtk_widget_get_allocation(boardWidget, &a);
1011 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1012 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1018 if(appData.logoSize)
1019 { // locate and read user logo
1021 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1022 ASSIGN(userLogo, buf);
1025 if (appData.animate || appData.animateDragging)
1028 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1029 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1031 /* [AS] Restore layout */
1032 if( wpMoveHistory.visible ) {
1036 if( wpEvalGraph.visible )
1041 if( wpEngineOutput.visible ) {
1042 EngineOutputPopUp();
1047 if (errorExitStatus == -1) {
1048 if (appData.icsActive) {
1049 /* We now wait until we see "login:" from the ICS before
1050 sending the logon script (problems with timestamp otherwise) */
1051 /*ICSInitScript();*/
1052 if (appData.icsInputBox) ICSInputBoxPopUp();
1056 signal(SIGWINCH, TermSizeSigHandler);
1058 signal(SIGINT, IntSigHandler);
1059 signal(SIGTERM, IntSigHandler);
1060 if (*appData.cmailGameName != NULLCHAR) {
1061 signal(SIGUSR1, CmailSigHandler);
1065 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1068 // XtSetKeyboardFocus(shellWidget, formWidget);
1070 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1073 /* check for GTK events and process them */
1076 gtk_main_iteration();
1079 if (appData.debugMode) fclose(debugFP); // [DM] debug
1084 TermSizeSigHandler (int sig)
1090 IntSigHandler (int sig)
1096 CmailSigHandler (int sig)
1101 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1103 /* Activate call-back function CmailSigHandlerCallBack() */
1104 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1106 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1110 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1113 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1115 /**** end signal code ****/
1118 #define Abs(n) ((n)<0 ? -(n) : (n))
1122 InsertPxlSize (char *pattern, int targetPxlSize)
1124 char *base_fnt_lst, strInt[12], *p, *q;
1125 int alternatives, i, len, strIntLen;
1128 * Replace the "*" (if present) in the pixel-size slot of each
1129 * alternative with the targetPxlSize.
1133 while ((p = strchr(p, ',')) != NULL) {
1137 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1138 strIntLen = strlen(strInt);
1139 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1143 while (alternatives--) {
1144 char *comma = strchr(p, ',');
1145 for (i=0; i<14; i++) {
1146 char *hyphen = strchr(p, '-');
1148 if (comma && hyphen > comma) break;
1149 len = hyphen + 1 - p;
1150 if (i == 7 && *p == '*' && len == 2) {
1152 memcpy(q, strInt, strIntLen);
1162 len = comma + 1 - p;
1169 return base_fnt_lst;
1174 CreateFontSet (char *base_fnt_lst)
1177 char **missing_list;
1181 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1182 &missing_list, &missing_count, &def_string);
1183 if (appData.debugMode) {
1185 XFontStruct **font_struct_list;
1186 char **font_name_list;
1187 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1189 fprintf(debugFP, " got list %s, locale %s\n",
1190 XBaseFontNameListOfFontSet(fntSet),
1191 XLocaleOfFontSet(fntSet));
1192 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1193 for (i = 0; i < count; i++) {
1194 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1197 for (i = 0; i < missing_count; i++) {
1198 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1201 if (fntSet == NULL) {
1202 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1208 #else // not ENABLE_NLS
1210 * Find a font that matches "pattern" that is as close as
1211 * possible to the targetPxlSize. Prefer fonts that are k
1212 * pixels smaller to fonts that are k pixels larger. The
1213 * pattern must be in the X Consortium standard format,
1214 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1215 * The return value should be freed with XtFree when no
1219 FindFont (char *pattern, int targetPxlSize)
1221 char **fonts, *p, *best, *scalable, *scalableTail;
1222 int i, j, nfonts, minerr, err, pxlSize;
1225 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1227 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1228 programName, pattern);
1235 for (i=0; i<nfonts; i++) {
1238 if (*p != '-') continue;
1240 if (*p == NULLCHAR) break;
1241 if (*p++ == '-') j++;
1243 if (j < 7) continue;
1246 scalable = fonts[i];
1249 err = pxlSize - targetPxlSize;
1250 if (Abs(err) < Abs(minerr) ||
1251 (minerr > 0 && err < 0 && -err == minerr)) {
1257 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1258 /* If the error is too big and there is a scalable font,
1259 use the scalable font. */
1260 int headlen = scalableTail - scalable;
1261 p = (char *) XtMalloc(strlen(scalable) + 10);
1262 while (isdigit(*scalableTail)) scalableTail++;
1263 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1265 p = (char *) XtMalloc(strlen(best) + 2);
1266 safeStrCpy(p, best, strlen(best)+1 );
1268 if (appData.debugMode) {
1269 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1270 pattern, targetPxlSize, p);
1272 XFreeFontNames(fonts);
1279 EnableNamedMenuItem (char *menuRef, int state)
1281 MenuItem *item = MenuNameToItem(menuRef);
1283 if(item) gtk_widget_set_sensitive(item->handle, state);
1287 EnableButtonBar (int state)
1290 XtSetSensitive(optList[W_BUTTON].handle, state);
1296 SetMenuEnables (Enables *enab)
1298 while (enab->name != NULL) {
1299 EnableNamedMenuItem(enab->name, enab->value);
1304 gboolean KeyPressProc(window, eventkey, data)
1306 GdkEventKey *eventkey;
1310 MoveTypeInProc(eventkey); // pop up for typed in moves
1313 /* check for other key values */
1314 switch(eventkey->keyval) {
1326 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1327 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1329 if(*nprms == 0) return;
1330 item = MenuNameToItem(prms[0]);
1331 if(item) ((MenuProc *) item->proc) ();
1345 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1346 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1347 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1348 dmEnables[i].piece);
1349 XtSetSensitive(entry, p != NULL || !appData.testLegality
1350 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1351 && !appData.icsActive));
1353 while (p && *p++ == dmEnables[i].piece) count++;
1354 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1356 XtSetArg(args[j], XtNlabel, label); j++;
1357 XtSetValues(entry, args, j);
1363 do_flash_delay (unsigned long msec)
1369 FlashDelay (int flash_delay)
1371 if(flash_delay) do_flash_delay(flash_delay);
1375 Fraction (int x, int start, int stop)
1377 double f = ((double) x - start)/(stop - start);
1378 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1382 static WindowPlacement wpNew;
1385 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1387 int touch=0, fudge = 2, f = 2;
1388 GetActualPlacement(sh, wp);
1389 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1390 if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x) < fudge) touch = 2; else // left touch
1391 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1392 if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y) < fudge) touch = 4; // top touch
1393 //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);
1394 if(!touch ) return; // only windows that touch co-move
1395 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1396 int heightInc = wpNew.height - wpMain.height;
1397 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1398 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1399 wp->y += fracTop * heightInc;
1400 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1402 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1404 wp->height += heightInc;
1405 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1406 int widthInc = wpNew.width - wpMain.width;
1407 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1408 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1409 wp->y += fracLeft * widthInc;
1410 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1412 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1414 wp->width += widthInc;
1416 wp->x += wpNew.x - wpMain.x;
1417 wp->y += wpNew.y - wpMain.y;
1418 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1419 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1421 XtSetArg(args[j], XtNx, wp->x); j++;
1422 XtSetArg(args[j], XtNy, wp->y); j++;
1423 XtSetValues(sh, args, j);
1425 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1426 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1427 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1431 ReSize (WindowPlacement *wp)
1434 int sqx, sqy, w, h, hc, lg = lineGap;
1435 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1436 hc = a.height; // clock height can depend on single / double line clock text!
1437 if(clockKludge == a.height) return; // wait for clock to get final size at startup
1438 if(clockKludge) { // clock height OK now; calculate desired initial board height
1440 wp->height = BOARD_HEIGHT * (squareSize + lineGap) + lineGap + marginH + hc;
1442 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1443 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1444 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1445 if(sqy < sqx) sqx = sqy;
1446 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1447 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1448 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1449 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1450 if(sqy < sqx) sqx = sqy;
1452 if(sqx != squareSize) {
1453 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1454 squareSize = sqx; // adopt new square size
1455 CreatePNGPieces(); // make newly scaled pieces
1456 InitDrawingSizes(0, 0); // creates grid etc.
1457 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1458 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1459 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1460 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1461 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1464 static guint delayedDragTag = 0;
1473 // GetActualPlacement(shellWidget, &wpNew);
1474 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1475 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1476 busy = 0; return; // false alarm
1479 if(appData.useStickyWindows) {
1480 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1481 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1482 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1483 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1486 DrawPosition(True, NULL);
1487 if(delayedDragTag) g_source_remove(delayedDragTag);
1488 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1495 //printf("old timr = %d\n", delayedDragTag);
1496 if(delayedDragTag) g_source_remove(delayedDragTag);
1497 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1498 //printf("new timr = %d\n", delayedDragTag);
1502 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1504 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1506 wpNew.x = event->configure.x;
1507 wpNew.y = event->configure.y;
1508 wpNew.width = event->configure.width;
1509 wpNew.height = event->configure.height;
1510 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1516 /* Disable all user input other than deleting the window */
1517 static int frozen = 0;
1523 /* Grab by a widget that doesn't accept input */
1524 gtk_grab_add(optList[W_MESSG].handle);
1528 /* Undo a FreezeUI */
1532 if (!frozen) return;
1533 gtk_grab_remove(optList[W_MESSG].handle);
1540 static int oldPausing = FALSE;
1541 static GameMode oldmode = (GameMode) -1;
1543 if (!boardWidget) return;
1545 if (pausing != oldPausing) {
1546 oldPausing = pausing;
1547 MarkMenuItem("Mode.Pause", pausing);
1549 if (appData.showButtonBar) {
1550 /* Always toggle, don't set. Previous code messes up when
1551 invoked while the button is pressed, as releasing it
1552 toggles the state again. */
1554 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1555 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1559 wname = ModeToWidgetName(oldmode);
1560 if (wname != NULL) {
1561 MarkMenuItem(wname, False);
1563 wname = ModeToWidgetName(gameMode);
1564 if (wname != NULL) {
1565 MarkMenuItem(wname, True);
1568 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1570 /* Maybe all the enables should be handled here, not just this one */
1571 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1573 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1578 * Button/menu procedures
1581 void CopyFileToClipboard(gchar *filename)
1583 gchar *selection_tmp;
1587 FILE* f = fopen(filename, "r");
1590 if (f == NULL) return;
1594 selection_tmp = g_try_malloc(len + 1);
1595 if (selection_tmp == NULL) {
1596 printf("Malloc failed in CopyFileToClipboard\n");
1599 count = fread(selection_tmp, 1, len, f);
1602 g_free(selection_tmp);
1605 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1607 // copy selection_tmp to clipboard
1608 GdkDisplay *gdisp = gdk_display_get_default();
1610 g_free(selection_tmp);
1613 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1614 gtk_clipboard_set_text(cb, selection_tmp, -1);
1615 g_free(selection_tmp);
1619 CopySomething (char *src)
1621 GdkDisplay *gdisp = gdk_display_get_default();
1623 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1624 if (gdisp == NULL) return;
1625 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1626 gtk_clipboard_set_text(cb, src, -1);
1630 PastePositionProc ()
1632 GdkDisplay *gdisp = gdk_display_get_default();
1636 if (gdisp == NULL) return;
1637 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1638 fenstr = gtk_clipboard_wait_for_text(cb);
1639 if (fenstr==NULL) return; // nothing had been selected to copy
1640 EditPositionPasteFEN(fenstr);
1652 // get game from clipboard
1653 GdkDisplay *gdisp = gdk_display_get_default();
1654 if (gdisp == NULL) return;
1655 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1656 text = gtk_clipboard_wait_for_text(cb);
1657 if (text == NULL) return; // nothing to paste
1660 // write to temp file
1661 if (text == NULL || len == 0) {
1662 return; //nothing to paste
1664 f = fopen(gamePasteFilename, "w");
1666 DisplayError(_("Can't open temp file"), errno);
1669 fwrite(text, 1, len, f);
1673 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1680 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1686 void MoveTypeInProc(eventkey)
1687 GdkEventKey *eventkey;
1691 // ingnore if ctrl or alt is pressed
1692 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
1696 buf[0]=eventkey->keyval;
1698 if (eventkey->keyval > 32 && eventkey->keyval < 256)
1704 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1706 if (!TempBackwardActive) {
1707 TempBackwardActive = True;
1713 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1715 /* Check to see if triggered by a key release event for a repeating key.
1716 * If so the next queued event will be a key press of the same key at the same time */
1717 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1719 XPeekEvent(xDisplay, &next);
1720 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1721 next.xkey.keycode == event->xkey.keycode)
1725 TempBackwardActive = False;
1729 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1730 { // called as key binding
1733 if (nprms && *nprms > 0)
1737 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
1744 { // called from menu
1746 ManInner(NULL, NULL, NULL, NULL);
1751 SetWindowTitle (char *text, char *title, char *icon)
1756 if (appData.titleInWindow) {
1758 XtSetArg(args[i], XtNlabel, text); i++;
1759 XtSetValues(titleWidget, args, i);
1762 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1763 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1764 XtSetValues(shellWidget, args, i);
1765 XSync(xDisplay, False);
1767 if (appData.titleInWindow) {
1768 SetWidgetLabel(titleWidget, text);
1770 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1775 DisplayIcsInteractionTitle (String message)
1778 if (oldICSInteractionTitle == NULL) {
1779 /* Magic to find the old window title, adapted from vim */
1780 char *wina = getenv("WINDOWID");
1782 Window win = (Window) atoi(wina);
1783 Window root, parent, *children;
1784 unsigned int nchildren;
1785 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1787 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1788 if (!XQueryTree(xDisplay, win, &root, &parent,
1789 &children, &nchildren)) break;
1790 if (children) XFree((void *)children);
1791 if (parent == root || parent == 0) break;
1794 XSetErrorHandler(oldHandler);
1796 if (oldICSInteractionTitle == NULL) {
1797 oldICSInteractionTitle = "xterm";
1800 printf("\033]0;%s\007", message);
1807 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1809 GtkWidget *w = (GtkWidget *) opt->handle;
1816 strcpy(bgcolor, "black");
1817 strcpy(fgcolor, "white");
1819 strcpy(bgcolor, "white");
1820 strcpy(fgcolor, "black");
1823 appData.lowTimeWarning &&
1824 (timer / 1000) < appData.icsAlarmTime) {
1825 strcpy(fgcolor, appData.lowTimeWarningColor);
1828 gdk_color_parse( bgcolor, &col );
1829 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1831 if (appData.clockMode) {
1832 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1833 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1835 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
1836 bgcolor, fgcolor, color);
1838 gtk_label_set_markup(GTK_LABEL(w), markup);
1842 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1845 SetClockIcon (int color)
1847 GdkPixbuf *pm = *clockIcons[color];
1848 if (mainwindowIcon != pm) {
1849 mainwindowIcon = pm;
1850 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1854 #define INPUT_SOURCE_BUF_SIZE 8192
1863 char buf[INPUT_SOURCE_BUF_SIZE];
1868 DoInputCallback(io, cond, data)
1873 /* read input from one of the input source (for example a chess program, ICS, etc).
1874 * and call a function that will handle the input
1881 /* All information (callback function, file descriptor, etc) is
1882 * saved in an InputSource structure
1884 InputSource *is = (InputSource *) data;
1886 if (is->lineByLine) {
1887 count = read(is->fd, is->unused,
1888 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1890 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1893 is->unused += count;
1895 /* break input into lines and call the callback function on each
1898 while (p < is->unused) {
1899 q = memchr(p, '\n', is->unused - p);
1900 if (q == NULL) break;
1902 (is->func)(is, is->closure, p, q - p, 0);
1905 /* remember not yet used part of the buffer */
1907 while (p < is->unused) {
1912 /* read maximum length of input buffer and send the whole buffer
1913 * to the callback function
1915 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1920 (is->func)(is, is->closure, is->buf, count, error);
1922 return True; // Must return true or the watch will be removed
1925 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1932 GIOChannel *channel;
1933 ChildProc *cp = (ChildProc *) pr;
1935 is = (InputSource *) calloc(1, sizeof(InputSource));
1936 is->lineByLine = lineByLine;
1940 is->fd = fileno(stdin);
1942 is->kind = cp->kind;
1943 is->fd = cp->fdFrom;
1946 is->unused = is->buf;
1950 /* GTK-TODO: will this work on windows?*/
1952 channel = g_io_channel_unix_new(is->fd);
1953 g_io_channel_set_close_on_unref (channel, TRUE);
1954 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1956 is->closure = closure;
1957 return (InputSourceRef) is;
1962 RemoveInputSource(isr)
1965 InputSource *is = (InputSource *) isr;
1967 if (is->sid == 0) return;
1968 g_source_remove(is->sid);
1975 static Boolean frameWaiting;
1978 FrameAlarm (int sig)
1980 frameWaiting = False;
1981 /* In case System-V style signals. Needed?? */
1982 signal(SIGALRM, FrameAlarm);
1986 FrameDelay (int time)
1988 struct itimerval delay;
1991 frameWaiting = True;
1992 signal(SIGALRM, FrameAlarm);
1993 delay.it_interval.tv_sec =
1994 delay.it_value.tv_sec = time / 1000;
1995 delay.it_interval.tv_usec =
1996 delay.it_value.tv_usec = (time % 1000) * 1000;
1997 setitimer(ITIMER_REAL, &delay, NULL);
1998 while (frameWaiting) pause();
1999 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2000 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2001 setitimer(ITIMER_REAL, &delay, NULL);
2008 FrameDelay (int time)
2011 XSync(xDisplay, False);
2013 // gtk_main_iteration_do(False);
2016 usleep(time * 1000);
2022 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2024 char buf[MSG_SIZ], *logoName = buf;
2025 if(appData.logo[n][0]) {
2026 logoName = appData.logo[n];
2027 } else if(appData.autoLogo) {
2028 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2029 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2030 } else if(appData.directory[n] && appData.directory[n][0]) {
2031 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2035 { ASSIGN(cps->programLogo, logoName); }
2039 UpdateLogos (int displ)
2041 if(optList[W_WHITE-1].handle == NULL) return;
2042 LoadLogo(&first, 0, 0);
2043 LoadLogo(&second, 1, appData.icsActive);
2044 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2048 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2059 GtkFileFilter *gtkfilter;
2060 GtkFileFilter *gtkfilter_all;
2062 char fileext[10] = "";
2063 char *result = NULL;
2066 /* make a copy of the filter string, so that strtok can work with it*/
2067 cp = strndup(filter,strlen(filter));
2069 /* add filters for file extensions */
2070 gtkfilter = gtk_file_filter_new();
2071 gtkfilter_all = gtk_file_filter_new();
2073 /* one filter to show everything */
2074 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2075 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2077 /* add filter if present */
2078 result = strtok(cp, space);
2079 while( result != NULL ) {
2080 snprintf(fileext,10,"*%s",result);
2081 result = strtok( NULL, space );
2082 gtk_file_filter_add_pattern(gtkfilter, fileext);
2085 /* second filter to only show what's useful */
2086 gtk_file_filter_set_name (gtkfilter,filter);
2088 if (openMode[0] == 'r')
2090 dialog = gtk_file_chooser_dialog_new (label,
2092 GTK_FILE_CHOOSER_ACTION_OPEN,
2093 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2094 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2099 dialog = gtk_file_chooser_dialog_new (label,
2101 GTK_FILE_CHOOSER_ACTION_SAVE,
2102 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2103 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2105 /* add filename suggestions */
2106 if (strlen(def) > 0 )
2107 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2109 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2113 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2114 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2115 /* activate filter */
2116 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2118 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2123 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2126 f = fopen(filename, openMode);
2129 DisplayError(_("Failed to open file"), errno);
2133 /* TODO add indec */
2135 ASSIGN(*name, filename);
2136 ScheduleDelayedEvent(DelayedLoad, 50);
2141 gtk_widget_destroy (dialog);