2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
64 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.h>
69 # if HAVE_SYS_SOCKET_H
70 # include <sys/socket.h>
71 # include <netinet/in.h>
73 # else /* not HAVE_SYS_SOCKET_H */
74 # if HAVE_LAN_SOCKET_H
75 # include <lan/socket.h>
77 # include <lan/netdb.h>
78 # else /* not HAVE_LAN_SOCKET_H */
79 # define OMIT_SOCKETS 1
80 # endif /* not HAVE_LAN_SOCKET_H */
81 # endif /* not HAVE_SYS_SOCKET_H */
82 #endif /* !OMIT_SOCKETS */
87 #else /* not STDC_HEADERS */
88 extern char *getenv();
91 # else /* not HAVE_STRING_H */
93 # endif /* not HAVE_STRING_H */
94 #endif /* not STDC_HEADERS */
97 # include <sys/fcntl.h>
98 #else /* not HAVE_SYS_FCNTL_H */
101 # endif /* HAVE_FCNTL_H */
102 #endif /* not HAVE_SYS_FCNTL_H */
104 #if HAVE_SYS_SYSTEMINFO_H
105 # include <sys/systeminfo.h>
106 #endif /* HAVE_SYS_SYSTEMINFO_H */
108 #if TIME_WITH_SYS_TIME
109 # include <sys/time.h>
113 # include <sys/time.h>
124 # include <sys/wait.h>
129 # define NAMLEN(dirent) strlen((dirent)->d_name)
130 # define HAVE_DIR_STRUCT
132 # define dirent direct
133 # define NAMLEN(dirent) (dirent)->d_namlen
135 # include <sys/ndir.h>
136 # define HAVE_DIR_STRUCT
139 # include <sys/dir.h>
140 # define HAVE_DIR_STRUCT
144 # define HAVE_DIR_STRUCT
152 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
155 #include "frontend.h"
157 #include "backendz.h"
165 #include "engineoutput.h"
175 #define usleep(t) _sleep2(((t)+500)/1000)
179 # define _(s) gettext (s)
180 # define N_(s) gettext_noop (s)
186 int main P((int argc, char **argv));
187 RETSIGTYPE CmailSigHandler P((int sig));
188 RETSIGTYPE IntSigHandler P((int sig));
189 RETSIGTYPE TermSizeSigHandler P((int sig));
191 char *InsertPxlSize P((char *pattern, int targetPxlSize));
192 XFontSet CreateFontSet P((char *base_fnt_lst));
194 char *FindFont P((char *pattern, int targetPxlSize));
196 void DelayedDrag P((void));
197 void ICSInputBoxPopUp P((void));
198 void MoveTypeInProc P((GdkEventKey *eventkey));
199 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
200 Boolean TempBackwardActive = False;
201 void DisplayMove P((int moveNumber));
202 void update_ics_width P(());
203 int CopyMemoProc P(());
204 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
208 XFontSet fontSet, clockFontSet;
211 XFontStruct *clockFontStruct;
213 Font coordFontID, countFontID;
214 XFontStruct *coordFontStruct, *countFontStruct;
216 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
217 GtkWidget *mainwindow;
219 Option *optList; // contains all widgets of main window
222 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
225 static GdkPixbuf *mainwindowIcon=NULL;
226 static GdkPixbuf *WhiteIcon=NULL;
227 static GdkPixbuf *BlackIcon=NULL;
229 /* key board accelerators */
230 GtkAccelGroup *GtkAccelerators;
232 typedef unsigned int BoardSize;
234 Boolean chessProgram;
236 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
237 int smallLayout = 0, tinyLayout = 0,
238 marginW, marginH, // [HGM] for run-time resizing
239 fromX = -1, fromY = -1, toX, toY, commentUp = False,
240 errorExitStatus = -1, defaultLineGap;
242 Dimension textHeight;
244 char *chessDir, *programName, *programVersion;
245 Boolean alwaysOnTop = False;
246 char *icsTextMenuString;
248 char *firstChessProgramNames;
249 char *secondChessProgramNames;
251 WindowPlacement wpMain;
252 WindowPlacement wpConsole;
253 WindowPlacement wpComment;
254 WindowPlacement wpMoveHistory;
255 WindowPlacement wpEvalGraph;
256 WindowPlacement wpEngineOutput;
257 WindowPlacement wpGameList;
258 WindowPlacement wpTags;
259 WindowPlacement wpDualBoard;
261 /* This magic number is the number of intermediate frames used
262 in each half of the animation. For short moves it's reduced
263 by 1. The total number of frames will be factor * 2 + 1. */
266 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
273 DropMenuEnables dmEnables[] = {
282 XtResource clientResources[] = {
283 { "flashCount", "flashCount", XtRInt, sizeof(int),
284 XtOffset(AppDataPtr, flashCount), XtRImmediate,
285 (XtPointer) FLASH_COUNT },
289 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
290 char globalTranslations[] =
291 ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
292 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
293 :<KeyDown>Return: TempBackwardProc() \n \
294 :<KeyUp>Return: TempForwardProc() \n";
296 char ICSInputTranslations[] =
297 "<Key>Up: UpKeyProc() \n "
298 "<Key>Down: DownKeyProc() \n "
299 "<Key>Return: EnterKeyProc() \n";
301 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
302 // as the widget is destroyed before the up-click can call extend-end
303 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
306 String xboardResources[] = {
307 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
315 gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
318 //---------------------------------------------------------------------------------------------------------
319 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
322 #define CW_USEDEFAULT (1<<31)
323 #define ICS_TEXT_MENU_SIZE 90
324 #define DEBUG_FILE "xboard.debug"
325 #define SetCurrentDirectory chdir
326 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
330 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
333 // front-end part of option handling
335 // [HGM] This platform-dependent table provides the location for storing the color info
336 extern char *crWhite, * crBlack;
340 &appData.whitePieceColor,
341 &appData.blackPieceColor,
342 &appData.lightSquareColor,
343 &appData.darkSquareColor,
344 &appData.highlightSquareColor,
345 &appData.premoveHighlightColor,
346 &appData.lowTimeWarningColor,
357 // [HGM] font: keep a font for each square size, even non-stndard ones
360 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
361 char *fontTable[NUM_FONTS][MAX_SIZE];
364 ParseFont (char *name, int number)
365 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
367 if(sscanf(name, "size%d:", &size)) {
368 // [HGM] font: font is meant for specific boardSize (likely from settings file);
369 // defer processing it until we know if it matches our board size
370 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
371 fontTable[number][size] = strdup(strchr(name, ':')+1);
372 fontValid[number][size] = True;
377 case 0: // CLOCK_FONT
378 appData.clockFont = strdup(name);
380 case 1: // MESSAGE_FONT
381 appData.font = strdup(name);
383 case 2: // COORD_FONT
384 appData.coordFont = strdup(name);
389 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
394 { // only 2 fonts currently
395 appData.clockFont = CLOCK_FONT_NAME;
396 appData.coordFont = COORD_FONT_NAME;
397 appData.font = DEFAULT_FONT_NAME;
402 { // no-op, until we identify the code for this already in XBoard and move it here
406 ParseColor (int n, char *name)
407 { // in XBoard, just copy the color-name string
408 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
412 ParseTextAttribs (ColorClass cc, char *s)
414 (&appData.colorShout)[cc] = strdup(s);
418 ParseBoardSize (void *addr, char *name)
420 appData.boardSize = strdup(name);
425 { // In XBoard the sound-playing program takes care of obtaining the actual sound
429 SetCommPortDefaults ()
430 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
433 // [HGM] args: these three cases taken out to stay in front-end
435 SaveFontArg (FILE *f, ArgDescriptor *ad)
438 int i, n = (int)(intptr_t)ad->argLoc;
440 case 0: // CLOCK_FONT
441 name = appData.clockFont;
443 case 1: // MESSAGE_FONT
446 case 2: // COORD_FONT
447 name = appData.coordFont;
452 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
453 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
454 fontTable[n][squareSize] = strdup(name);
455 fontValid[n][squareSize] = True;
458 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
459 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
464 { // nothing to do, as the sounds are at all times represented by their text-string names already
468 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
469 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
470 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
474 SaveColor (FILE *f, ArgDescriptor *ad)
475 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
476 if(colorVariable[(int)(intptr_t)ad->argLoc])
477 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
481 SaveBoardSize (FILE *f, char *name, void *addr)
482 { // wrapper to shield back-end from BoardSize & sizeInfo
483 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
487 ParseCommPortSettings (char *s)
488 { // no such option in XBoard (yet)
494 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
498 gtk_widget_get_allocation(shell, &a);
499 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
503 wp->height = a.height;
504 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
505 frameX = 3; frameY = 3; // remember to decide if windows touch
510 { // wrapper to shield use of window handles from back-end (make addressible by number?)
511 // In XBoard this will have to wait until awareness of window parameters is implemented
512 GetActualPlacement(shellWidget, &wpMain);
513 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
514 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
515 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
516 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
517 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
518 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
522 PrintCommPortSettings (FILE *f, char *name)
523 { // This option does not exist in XBoard
527 EnsureOnScreen (int *x, int *y, int minX, int minY)
534 { // [HGM] args: allows testing if main window is realized from back-end
535 return DialogExists(BoardWindow);
539 PopUpStartupDialog ()
540 { // start menu not implemented in XBoard
544 ConvertToLine (int argc, char **argv)
546 static char line[128*1024], buf[1024];
550 for(i=1; i<argc; i++)
552 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
553 && argv[i][0] != '{' )
554 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
556 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
557 strncat(line, buf, 128*1024 - strlen(line) - 1 );
560 line[strlen(line)-1] = NULLCHAR;
564 //--------------------------------------------------------------------------------------------
567 ResizeBoardWindow (int w, int h, int inhibit)
569 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
571 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
576 { // dummy, as the GTK code does not make colors in advance
581 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
582 { // determine what fonts to use, and create them
587 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
588 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
589 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
590 appData.font = fontTable[MESSAGE_FONT][squareSize];
591 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
592 appData.coordFont = fontTable[COORD_FONT][squareSize];
595 appData.font = InsertPxlSize(appData.font, fontPxlSize);
596 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
597 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
598 fontSet = CreateFontSet(appData.font);
599 clockFontSet = CreateFontSet(appData.clockFont);
601 /* For the coordFont, use the 0th font of the fontset. */
602 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
603 XFontStruct **font_struct_list;
604 XFontSetExtents *fontSize;
605 char **font_name_list;
606 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
607 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
608 coordFontStruct = XQueryFont(xDisplay, coordFontID);
609 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
610 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
613 appData.font = FindFont(appData.font, fontPxlSize);
614 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
615 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
616 clockFontID = XLoadFont(xDisplay, appData.clockFont);
617 clockFontStruct = XQueryFont(xDisplay, clockFontID);
618 coordFontID = XLoadFont(xDisplay, appData.coordFont);
619 coordFontStruct = XQueryFont(xDisplay, coordFontID);
620 // textHeight in !NLS mode!
622 countFontID = coordFontID; // [HGM] holdings
623 countFontStruct = coordFontStruct;
625 xdb = XtDatabase(xDisplay);
627 XrmPutLineResource(&xdb, "*international: True");
628 vTo.size = sizeof(XFontSet);
629 vTo.addr = (XtPointer) &fontSet;
630 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
632 XrmPutStringResource(&xdb, "*font", appData.font);
643 case ArgInt: p = " N"; break;
644 case ArgString: p = " STR"; break;
645 case ArgBoolean: p = " TF"; break;
646 case ArgSettingsFilename:
647 case ArgBackupSettingsFile:
648 case ArgFilename: p = " FILE"; break;
649 case ArgX: p = " Nx"; break;
650 case ArgY: p = " Ny"; break;
651 case ArgAttribs: p = " TEXTCOL"; break;
652 case ArgColor: p = " COL"; break;
653 case ArgFont: p = " FONT"; break;
654 case ArgBoardSize: p = " SIZE"; break;
655 case ArgFloat: p = " FLOAT"; break;
660 case ArgCommSettings:
671 ArgDescriptor *q, *p = argDescriptors+5;
672 printf("\nXBoard accepts the following options:\n"
673 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
674 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
675 " SIZE = board-size spec(s)\n"
676 " Within parentheses are short forms, or options to set to true or false.\n"
677 " Persistent options (saved in the settings file) are marked with *)\n\n");
679 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
680 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
681 if(p->save) strcat(buf+len, "*");
682 for(q=p+1; q->argLoc == p->argLoc; q++) {
683 if(q->argName[0] == '-') continue;
684 strcat(buf+len, q == p+1 ? " (" : " ");
685 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
687 if(q != p+1) strcat(buf+len, ")");
689 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
692 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
696 SlaveResize (Option *opt)
698 static int slaveW, slaveH, w, h;
701 gtk_widget_get_allocation(shells[DummyDlg], &a);
702 w = a.width; h = a.height;
703 gtk_widget_get_allocation(opt->handle, &a);
704 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
705 slaveH = h - a.height + 13;
707 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
711 main (int argc, char **argv)
713 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
714 int boardWidth, boardHeight, w, h;
716 int forceMono = False;
718 srandom(time(0)); // [HGM] book: make random truly random
720 setbuf(stdout, NULL);
721 setbuf(stderr, NULL);
724 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
725 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
729 if(argc > 1 && !strcmp(argv[1], "--help" )) {
735 gtk_init (&argc, &argv);
737 /* set up keyboard accelerators group */
738 GtkAccelerators = gtk_accel_group_new();
740 programName = strrchr(argv[0], '/');
741 if (programName == NULL)
742 programName = argv[0];
747 // if (appData.debugMode) {
748 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
751 bindtextdomain(PACKAGE, LOCALEDIR);
752 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
756 appData.boardSize = "";
757 InitAppData(ConvertToLine(argc, argv));
759 if (p == NULL) p = "/tmp";
760 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
761 gameCopyFilename = (char*) malloc(i);
762 gamePasteFilename = (char*) malloc(i);
763 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
764 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
766 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
767 static char buf[MSG_SIZ];
768 EscapeExpand(buf, appData.firstInitString);
769 appData.firstInitString = strdup(buf);
770 EscapeExpand(buf, appData.secondInitString);
771 appData.secondInitString = strdup(buf);
772 EscapeExpand(buf, appData.firstComputerString);
773 appData.firstComputerString = strdup(buf);
774 EscapeExpand(buf, appData.secondComputerString);
775 appData.secondComputerString = strdup(buf);
778 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
781 if (chdir(chessDir) != 0) {
782 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
788 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
789 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
790 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
791 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
794 setbuf(debugFP, NULL);
798 if (appData.debugMode) {
799 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
803 /* [HGM,HR] make sure board size is acceptable */
804 if(appData.NrFiles > BOARD_FILES ||
805 appData.NrRanks > BOARD_RANKS )
806 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
809 /* This feature does not work; animation needs a rewrite */
810 appData.highlightDragging = FALSE;
814 gameInfo.variant = StringToVariant(appData.variant);
818 * determine size, based on supplied or remembered -size, or screen size
820 if (isdigit(appData.boardSize[0])) {
821 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
822 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
823 &fontPxlSize, &smallLayout, &tinyLayout);
825 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
826 programName, appData.boardSize);
830 /* Find some defaults; use the nearest known size */
831 SizeDefaults *szd, *nearest;
832 int distance = 99999;
833 nearest = szd = sizeDefaults;
834 while (szd->name != NULL) {
835 if (abs(szd->squareSize - squareSize) < distance) {
837 distance = abs(szd->squareSize - squareSize);
838 if (distance == 0) break;
842 if (i < 2) lineGap = nearest->lineGap;
843 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
844 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
845 if (i < 5) fontPxlSize = nearest->fontPxlSize;
846 if (i < 6) smallLayout = nearest->smallLayout;
847 if (i < 7) tinyLayout = nearest->tinyLayout;
850 SizeDefaults *szd = sizeDefaults;
851 if (*appData.boardSize == NULLCHAR) {
852 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
853 guint screenwidth = gdk_screen_get_width(screen);
854 guint screenheight = gdk_screen_get_height(screen);
855 while (screenwidth < szd->minScreenSize ||
856 screenheight < szd->minScreenSize) {
859 if (szd->name == NULL) szd--;
860 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
862 while (szd->name != NULL &&
863 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
864 if (szd->name == NULL) {
865 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
866 programName, appData.boardSize);
870 squareSize = szd->squareSize;
871 lineGap = szd->lineGap;
872 clockFontPxlSize = szd->clockFontPxlSize;
873 coordFontPxlSize = szd->coordFontPxlSize;
874 fontPxlSize = szd->fontPxlSize;
875 smallLayout = szd->smallLayout;
876 tinyLayout = szd->tinyLayout;
877 // [HGM] font: use defaults from settings file if available and not overruled
880 defaultLineGap = lineGap;
881 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
883 /* [HR] height treated separately (hacked) */
884 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
885 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
888 * Determine what fonts to use.
891 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
895 * Detect if there are not enough colors available and adapt.
898 if (DefaultDepth(xDisplay, xScreen) <= 2) {
899 appData.monoMode = True;
903 forceMono = MakeColors();
906 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
908 appData.monoMode = True;
911 ParseIcsTextColors();
917 layoutName = "tinyLayout";
918 } else if (smallLayout) {
919 layoutName = "smallLayout";
921 layoutName = "normalLayout";
924 optList = BoardPopUp(squareSize, lineGap, (void*)
934 InitDrawingHandle(optList + W_BOARD);
935 shellWidget = shells[BoardWindow];
936 currBoard = &optList[W_BOARD];
937 boardWidget = optList[W_BOARD].handle;
938 menuBarWidget = optList[W_MENU].handle;
939 dropMenu = optList[W_DROP].handle;
940 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
942 formWidget = XtParent(boardWidget);
943 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
944 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
945 XtGetValues(optList[W_WHITE].handle, args, 2);
946 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
947 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
948 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
949 XtGetValues(optList[W_PAUSE].handle, args, 2);
953 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
954 // not need to go into InitDrawingSizes().
958 // add accelerators to main shell
959 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
962 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
964 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
965 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
966 mainwindowIcon = WhiteIcon;
967 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
971 * Create a cursor for the board widget.
974 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
975 XChangeWindowAttributes(xDisplay, xBoardWindow,
976 CWCursor, &window_attributes);
980 * Inhibit shell resizing.
983 shellArgs[0].value = (XtArgVal) &w;
984 shellArgs[1].value = (XtArgVal) &h;
985 XtGetValues(shellWidget, shellArgs, 2);
986 shellArgs[4].value = shellArgs[2].value = w;
987 shellArgs[5].value = shellArgs[3].value = h;
988 // XtSetValues(shellWidget, &shellArgs[2], 4);
992 gtk_widget_get_allocation(shells[BoardWindow], &a);
993 w = a.width; h = a.height;
994 //printf("start size (%d,%d), %dx%d\n", a.x, a.y, w, h);
995 gtk_widget_get_allocation(boardWidget, &a);
996 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
997 marginH = h - a.height + 13;
998 gtk_window_resize(GTK_WINDOW(shellWidget), marginW + boardWidth, marginH + boardHeight);
999 //printf("margins h=%d v=%d\n", marginW, marginH);
1005 if(appData.logoSize)
1006 { // locate and read user logo
1008 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1009 ASSIGN(userLogo, buf);
1012 if (appData.animate || appData.animateDragging)
1015 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1016 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1018 /* [AS] Restore layout */
1019 if( wpMoveHistory.visible ) {
1023 if( wpEvalGraph.visible )
1028 if( wpEngineOutput.visible ) {
1029 EngineOutputPopUp();
1034 if (errorExitStatus == -1) {
1035 if (appData.icsActive) {
1036 /* We now wait until we see "login:" from the ICS before
1037 sending the logon script (problems with timestamp otherwise) */
1038 /*ICSInitScript();*/
1039 if (appData.icsInputBox) ICSInputBoxPopUp();
1043 signal(SIGWINCH, TermSizeSigHandler);
1045 signal(SIGINT, IntSigHandler);
1046 signal(SIGTERM, IntSigHandler);
1047 if (*appData.cmailGameName != NULLCHAR) {
1048 signal(SIGUSR1, CmailSigHandler);
1052 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1055 // XtSetKeyboardFocus(shellWidget, formWidget);
1057 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1060 /* check for GTK events and process them */
1063 gtk_main_iteration();
1066 if (appData.debugMode) fclose(debugFP); // [DM] debug
1071 TermSizeSigHandler (int sig)
1077 IntSigHandler (int sig)
1083 CmailSigHandler (int sig)
1088 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1090 /* Activate call-back function CmailSigHandlerCallBack() */
1091 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1093 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1097 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1100 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1102 /**** end signal code ****/
1105 #define Abs(n) ((n)<0 ? -(n) : (n))
1109 InsertPxlSize (char *pattern, int targetPxlSize)
1111 char *base_fnt_lst, strInt[12], *p, *q;
1112 int alternatives, i, len, strIntLen;
1115 * Replace the "*" (if present) in the pixel-size slot of each
1116 * alternative with the targetPxlSize.
1120 while ((p = strchr(p, ',')) != NULL) {
1124 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1125 strIntLen = strlen(strInt);
1126 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1130 while (alternatives--) {
1131 char *comma = strchr(p, ',');
1132 for (i=0; i<14; i++) {
1133 char *hyphen = strchr(p, '-');
1135 if (comma && hyphen > comma) break;
1136 len = hyphen + 1 - p;
1137 if (i == 7 && *p == '*' && len == 2) {
1139 memcpy(q, strInt, strIntLen);
1149 len = comma + 1 - p;
1156 return base_fnt_lst;
1161 CreateFontSet (char *base_fnt_lst)
1164 char **missing_list;
1168 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1169 &missing_list, &missing_count, &def_string);
1170 if (appData.debugMode) {
1172 XFontStruct **font_struct_list;
1173 char **font_name_list;
1174 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1176 fprintf(debugFP, " got list %s, locale %s\n",
1177 XBaseFontNameListOfFontSet(fntSet),
1178 XLocaleOfFontSet(fntSet));
1179 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1180 for (i = 0; i < count; i++) {
1181 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1184 for (i = 0; i < missing_count; i++) {
1185 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1188 if (fntSet == NULL) {
1189 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1195 #else // not ENABLE_NLS
1197 * Find a font that matches "pattern" that is as close as
1198 * possible to the targetPxlSize. Prefer fonts that are k
1199 * pixels smaller to fonts that are k pixels larger. The
1200 * pattern must be in the X Consortium standard format,
1201 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1202 * The return value should be freed with XtFree when no
1206 FindFont (char *pattern, int targetPxlSize)
1208 char **fonts, *p, *best, *scalable, *scalableTail;
1209 int i, j, nfonts, minerr, err, pxlSize;
1212 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1214 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1215 programName, pattern);
1222 for (i=0; i<nfonts; i++) {
1225 if (*p != '-') continue;
1227 if (*p == NULLCHAR) break;
1228 if (*p++ == '-') j++;
1230 if (j < 7) continue;
1233 scalable = fonts[i];
1236 err = pxlSize - targetPxlSize;
1237 if (Abs(err) < Abs(minerr) ||
1238 (minerr > 0 && err < 0 && -err == minerr)) {
1244 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1245 /* If the error is too big and there is a scalable font,
1246 use the scalable font. */
1247 int headlen = scalableTail - scalable;
1248 p = (char *) XtMalloc(strlen(scalable) + 10);
1249 while (isdigit(*scalableTail)) scalableTail++;
1250 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1252 p = (char *) XtMalloc(strlen(best) + 2);
1253 safeStrCpy(p, best, strlen(best)+1 );
1255 if (appData.debugMode) {
1256 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1257 pattern, targetPxlSize, p);
1259 XFreeFontNames(fonts);
1266 EnableNamedMenuItem (char *menuRef, int state)
1268 MenuItem *item = MenuNameToItem(menuRef);
1270 if(item) gtk_widget_set_sensitive(item->handle, state);
1274 EnableButtonBar (int state)
1277 XtSetSensitive(optList[W_BUTTON].handle, state);
1283 SetMenuEnables (Enables *enab)
1285 while (enab->name != NULL) {
1286 EnableNamedMenuItem(enab->name, enab->value);
1291 gboolean KeyPressProc(window, eventkey, data)
1293 GdkEventKey *eventkey;
1297 MoveTypeInProc(eventkey); // pop up for typed in moves
1300 /* check for other key values */
1301 switch(eventkey->keyval) {
1313 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1314 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1316 if(*nprms == 0) return;
1317 item = MenuNameToItem(prms[0]);
1318 if(item) ((MenuProc *) item->proc) ();
1332 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1333 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1334 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1335 dmEnables[i].piece);
1336 XtSetSensitive(entry, p != NULL || !appData.testLegality
1337 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1338 && !appData.icsActive));
1340 while (p && *p++ == dmEnables[i].piece) count++;
1341 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1343 XtSetArg(args[j], XtNlabel, label); j++;
1344 XtSetValues(entry, args, j);
1350 do_flash_delay (unsigned long msec)
1356 FlashDelay (int flash_delay)
1358 if(flash_delay) do_flash_delay(flash_delay);
1362 Fraction (int x, int start, int stop)
1364 double f = ((double) x - start)/(stop - start);
1365 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1369 static WindowPlacement wpNew;
1372 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1374 int touch=0, fudge = 2, f = 2;
1375 GetActualPlacement(sh, wp);
1376 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1377 if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x) < fudge) touch = 2; else // left touch
1378 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1379 if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y) < fudge) touch = 4; // top touch
1380 //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);
1381 if(!touch ) return; // only windows that touch co-move
1382 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1383 int heightInc = wpNew.height - wpMain.height;
1384 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1385 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1386 wp->y += fracTop * heightInc;
1387 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1389 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1391 wp->height += heightInc;
1392 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1393 int widthInc = wpNew.width - wpMain.width;
1394 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1395 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1396 wp->y += fracLeft * widthInc;
1397 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1399 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1401 wp->width += widthInc;
1403 wp->x += wpNew.x - wpMain.x;
1404 wp->y += wpNew.y - wpMain.y;
1405 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1406 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1408 XtSetArg(args[j], XtNx, wp->x); j++;
1409 XtSetArg(args[j], XtNy, wp->y); j++;
1410 XtSetValues(sh, args, j);
1412 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1413 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1414 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1418 ReSize (WindowPlacement *wp)
1420 int sqx, sqy, w, h, lg = lineGap;
1421 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1422 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1423 sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1424 if(sqy < sqx) sqx = sqy;
1425 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1426 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1427 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1428 sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1429 if(sqy < sqx) sqx = sqy;
1431 if(sqx != squareSize) {
1432 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1433 squareSize = sqx; // adopt new square size
1434 CreatePNGPieces(); // make newly scaled pieces
1435 InitDrawingSizes(0, 0); // creates grid etc.
1436 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1437 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1438 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1439 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1440 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1443 static guint delayedDragTag = 0;
1452 // GetActualPlacement(shellWidget, &wpNew);
1453 //printf("drag proc (%d,%d) %dx%d\n", wpNew.x, wpNew.y, wpNew.width, wpNew.height);
1454 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1455 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1456 busy = 0; return; // false alarm
1459 if(appData.useStickyWindows) {
1460 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1461 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1462 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1463 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1466 DrawPosition(True, NULL);
1467 if(delayedDragTag) g_source_remove(delayedDragTag);
1468 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1475 //printf("old timr = %d\n", delayedDragTag);
1476 if(delayedDragTag) g_source_remove(delayedDragTag);
1477 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1478 //printf("new timr = %d\n", delayedDragTag);
1482 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1484 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1486 wpNew.x = event->configure.x;
1487 wpNew.y = event->configure.y;
1488 wpNew.width = event->configure.width;
1489 wpNew.height = event->configure.height;
1490 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1496 /* Disable all user input other than deleting the window */
1497 static int frozen = 0;
1503 /* Grab by a widget that doesn't accept input */
1504 gtk_grab_add(optList[W_MESSG].handle);
1508 /* Undo a FreezeUI */
1512 if (!frozen) return;
1513 gtk_grab_remove(optList[W_MESSG].handle);
1520 static int oldPausing = FALSE;
1521 static GameMode oldmode = (GameMode) -1;
1523 if (!boardWidget) return;
1525 if (pausing != oldPausing) {
1526 oldPausing = pausing;
1527 MarkMenuItem("Mode.Pause", pausing);
1529 if (appData.showButtonBar) {
1530 /* Always toggle, don't set. Previous code messes up when
1531 invoked while the button is pressed, as releasing it
1532 toggles the state again. */
1534 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1535 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1539 wname = ModeToWidgetName(oldmode);
1540 if (wname != NULL) {
1541 MarkMenuItem(wname, False);
1543 wname = ModeToWidgetName(gameMode);
1544 if (wname != NULL) {
1545 MarkMenuItem(wname, True);
1548 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1550 /* Maybe all the enables should be handled here, not just this one */
1551 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1553 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1558 * Button/menu procedures
1561 void CopyFileToClipboard(gchar *filename)
1563 gchar *selection_tmp;
1567 FILE* f = fopen(filename, "r");
1570 if (f == NULL) return;
1574 selection_tmp = g_try_malloc(len + 1);
1575 if (selection_tmp == NULL) {
1576 printf("Malloc failed in CopyFileToClipboard\n");
1579 count = fread(selection_tmp, 1, len, f);
1582 g_free(selection_tmp);
1585 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1587 // copy selection_tmp to clipboard
1588 GdkDisplay *gdisp = gdk_display_get_default();
1590 g_free(selection_tmp);
1593 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1594 gtk_clipboard_set_text(cb, selection_tmp, -1);
1595 g_free(selection_tmp);
1599 CopySomething (char *src)
1601 GdkDisplay *gdisp = gdk_display_get_default();
1603 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1604 if (gdisp == NULL) return;
1605 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1606 gtk_clipboard_set_text(cb, src, -1);
1610 PastePositionProc ()
1612 GdkDisplay *gdisp = gdk_display_get_default();
1616 if (gdisp == NULL) return;
1617 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1618 fenstr = gtk_clipboard_wait_for_text(cb);
1619 if (fenstr==NULL) return; // nothing had been selected to copy
1620 EditPositionPasteFEN(fenstr);
1632 // get game from clipboard
1633 GdkDisplay *gdisp = gdk_display_get_default();
1634 if (gdisp == NULL) return;
1635 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1636 text = gtk_clipboard_wait_for_text(cb);
1637 if (text == NULL) return; // nothing to paste
1640 // write to temp file
1641 if (text == NULL || len == 0) {
1642 return; //nothing to paste
1644 f = fopen(gamePasteFilename, "w");
1646 DisplayError(_("Can't open temp file"), errno);
1649 fwrite(text, 1, len, f);
1653 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1660 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1666 void MoveTypeInProc(eventkey)
1667 GdkEventKey *eventkey;
1671 // ingnore if ctrl or alt is pressed
1672 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
1676 buf[0]=eventkey->keyval;
1684 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1686 if (!TempBackwardActive) {
1687 TempBackwardActive = True;
1693 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1695 /* Check to see if triggered by a key release event for a repeating key.
1696 * If so the next queued event will be a key press of the same key at the same time */
1697 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1699 XPeekEvent(xDisplay, &next);
1700 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1701 next.xkey.keycode == event->xkey.keycode)
1705 TempBackwardActive = False;
1709 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1710 { // called as key binding
1713 if (nprms && *nprms > 0)
1717 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
1724 { // called from menu
1726 ManInner(NULL, NULL, NULL, NULL);
1731 SetWindowTitle (char *text, char *title, char *icon)
1736 if (appData.titleInWindow) {
1738 XtSetArg(args[i], XtNlabel, text); i++;
1739 XtSetValues(titleWidget, args, i);
1742 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1743 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1744 XtSetValues(shellWidget, args, i);
1745 XSync(xDisplay, False);
1747 if (appData.titleInWindow) {
1748 SetWidgetLabel(titleWidget, text);
1750 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1755 DisplayIcsInteractionTitle (String message)
1758 if (oldICSInteractionTitle == NULL) {
1759 /* Magic to find the old window title, adapted from vim */
1760 char *wina = getenv("WINDOWID");
1762 Window win = (Window) atoi(wina);
1763 Window root, parent, *children;
1764 unsigned int nchildren;
1765 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1767 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1768 if (!XQueryTree(xDisplay, win, &root, &parent,
1769 &children, &nchildren)) break;
1770 if (children) XFree((void *)children);
1771 if (parent == root || parent == 0) break;
1774 XSetErrorHandler(oldHandler);
1776 if (oldICSInteractionTitle == NULL) {
1777 oldICSInteractionTitle = "xterm";
1780 printf("\033]0;%s\007", message);
1787 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1789 GtkWidget *w = (GtkWidget *) opt->handle;
1796 strcpy(bgcolor, "black");
1797 strcpy(fgcolor, "white");
1799 strcpy(bgcolor, "white");
1800 strcpy(fgcolor, "black");
1803 appData.lowTimeWarning &&
1804 (timer / 1000) < appData.icsAlarmTime) {
1805 strcpy(fgcolor, appData.lowTimeWarningColor);
1808 gdk_color_parse( bgcolor, &col );
1809 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1811 if (appData.clockMode) {
1812 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1813 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1815 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
1816 bgcolor, fgcolor, color);
1818 gtk_label_set_markup(GTK_LABEL(w), markup);
1822 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1825 SetClockIcon (int color)
1827 GdkPixbuf *pm = *clockIcons[color];
1828 if (mainwindowIcon != pm) {
1829 mainwindowIcon = pm;
1830 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1834 #define INPUT_SOURCE_BUF_SIZE 8192
1843 char buf[INPUT_SOURCE_BUF_SIZE];
1848 DoInputCallback(io, cond, data)
1853 /* read input from one of the input source (for example a chess program, ICS, etc).
1854 * and call a function that will handle the input
1861 /* All information (callback function, file descriptor, etc) is
1862 * saved in an InputSource structure
1864 InputSource *is = (InputSource *) data;
1866 if (is->lineByLine) {
1867 count = read(is->fd, is->unused,
1868 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1870 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1873 is->unused += count;
1875 /* break input into lines and call the callback function on each
1878 while (p < is->unused) {
1879 q = memchr(p, '\n', is->unused - p);
1880 if (q == NULL) break;
1882 (is->func)(is, is->closure, p, q - p, 0);
1885 /* remember not yet used part of the buffer */
1887 while (p < is->unused) {
1892 /* read maximum length of input buffer and send the whole buffer
1893 * to the callback function
1895 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1900 (is->func)(is, is->closure, is->buf, count, error);
1902 return True; // Must return true or the watch will be removed
1905 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1912 GIOChannel *channel;
1913 ChildProc *cp = (ChildProc *) pr;
1915 is = (InputSource *) calloc(1, sizeof(InputSource));
1916 is->lineByLine = lineByLine;
1920 is->fd = fileno(stdin);
1922 is->kind = cp->kind;
1923 is->fd = cp->fdFrom;
1926 is->unused = is->buf;
1930 /* GTK-TODO: will this work on windows?*/
1932 channel = g_io_channel_unix_new(is->fd);
1933 g_io_channel_set_close_on_unref (channel, TRUE);
1934 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1936 is->closure = closure;
1937 return (InputSourceRef) is;
1942 RemoveInputSource(isr)
1945 InputSource *is = (InputSource *) isr;
1947 if (is->sid == 0) return;
1948 g_source_remove(is->sid);
1955 static Boolean frameWaiting;
1958 FrameAlarm (int sig)
1960 frameWaiting = False;
1961 /* In case System-V style signals. Needed?? */
1962 signal(SIGALRM, FrameAlarm);
1966 FrameDelay (int time)
1968 struct itimerval delay;
1971 frameWaiting = True;
1972 signal(SIGALRM, FrameAlarm);
1973 delay.it_interval.tv_sec =
1974 delay.it_value.tv_sec = time / 1000;
1975 delay.it_interval.tv_usec =
1976 delay.it_value.tv_usec = (time % 1000) * 1000;
1977 setitimer(ITIMER_REAL, &delay, NULL);
1978 while (frameWaiting) pause();
1979 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
1980 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
1981 setitimer(ITIMER_REAL, &delay, NULL);
1988 FrameDelay (int time)
1991 XSync(xDisplay, False);
1993 // gtk_main_iteration_do(False);
1996 usleep(time * 1000);
2002 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2004 char buf[MSG_SIZ], *logoName = buf;
2005 if(appData.logo[n][0]) {
2006 logoName = appData.logo[n];
2007 } else if(appData.autoLogo) {
2008 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2009 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2010 } else if(appData.directory[n] && appData.directory[n][0]) {
2011 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2015 { ASSIGN(cps->programLogo, logoName); }
2019 UpdateLogos (int displ)
2021 if(optList[W_WHITE-1].handle == NULL) return;
2022 LoadLogo(&first, 0, 0);
2023 LoadLogo(&second, 1, appData.icsActive);
2024 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2028 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2039 GtkFileFilter *gtkfilter;
2040 GtkFileFilter *gtkfilter_all;
2042 char fileext[10] = "";
2043 char *result = NULL;
2046 /* make a copy of the filter string, so that strtok can work with it*/
2047 cp = strndup(filter,strlen(filter));
2049 /* add filters for file extensions */
2050 gtkfilter = gtk_file_filter_new();
2051 gtkfilter_all = gtk_file_filter_new();
2053 /* one filter to show everything */
2054 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2055 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2057 /* add filter if present */
2058 result = strtok(cp, space);
2059 while( result != NULL ) {
2060 snprintf(fileext,10,"*%s",result);
2061 result = strtok( NULL, space );
2062 gtk_file_filter_add_pattern(gtkfilter, fileext);
2065 /* second filter to only show what's useful */
2066 gtk_file_filter_set_name (gtkfilter,filter);
2068 if (openMode[0] == 'r')
2070 dialog = gtk_file_chooser_dialog_new (label,
2072 GTK_FILE_CHOOSER_ACTION_OPEN,
2073 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2074 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2079 dialog = gtk_file_chooser_dialog_new (label,
2081 GTK_FILE_CHOOSER_ACTION_SAVE,
2082 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2083 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2085 /* add filename suggestions */
2086 if (strlen(def) > 0 )
2087 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2089 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2093 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2094 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2095 /* activate filter */
2096 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2098 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2103 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2106 f = fopen(filename, openMode);
2109 DisplayError(_("Failed to open file"), errno);
2113 /* TODO add indec */
2115 ASSIGN(*name, filename);
2116 ScheduleDelayedEvent(DelayedLoad, 50);
2121 gtk_widget_destroy (dialog);