2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
64 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.h>
69 # if HAVE_SYS_SOCKET_H
70 # include <sys/socket.h>
71 # include <netinet/in.h>
73 # else /* not HAVE_SYS_SOCKET_H */
74 # if HAVE_LAN_SOCKET_H
75 # include <lan/socket.h>
77 # include <lan/netdb.h>
78 # else /* not HAVE_LAN_SOCKET_H */
79 # define OMIT_SOCKETS 1
80 # endif /* not HAVE_LAN_SOCKET_H */
81 # endif /* not HAVE_SYS_SOCKET_H */
82 #endif /* !OMIT_SOCKETS */
87 #else /* not STDC_HEADERS */
88 extern char *getenv();
91 # else /* not HAVE_STRING_H */
93 # endif /* not HAVE_STRING_H */
94 #endif /* not STDC_HEADERS */
97 # include <sys/fcntl.h>
98 #else /* not HAVE_SYS_FCNTL_H */
101 # endif /* HAVE_FCNTL_H */
102 #endif /* not HAVE_SYS_FCNTL_H */
104 #if HAVE_SYS_SYSTEMINFO_H
105 # include <sys/systeminfo.h>
106 #endif /* HAVE_SYS_SYSTEMINFO_H */
108 #if TIME_WITH_SYS_TIME
109 # include <sys/time.h>
113 # include <sys/time.h>
124 # include <sys/wait.h>
129 # define NAMLEN(dirent) strlen((dirent)->d_name)
130 # define HAVE_DIR_STRUCT
132 # define dirent direct
133 # define NAMLEN(dirent) (dirent)->d_namlen
135 # include <sys/ndir.h>
136 # define HAVE_DIR_STRUCT
139 # include <sys/dir.h>
140 # define HAVE_DIR_STRUCT
144 # define HAVE_DIR_STRUCT
152 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
155 #include "frontend.h"
157 #include "backendz.h"
165 #include "engineoutput.h"
175 #define usleep(t) _sleep2(((t)+500)/1000)
179 # define _(s) gettext (s)
180 # define N_(s) gettext_noop (s)
186 int main P((int argc, char **argv));
187 RETSIGTYPE CmailSigHandler P((int sig));
188 RETSIGTYPE IntSigHandler P((int sig));
189 RETSIGTYPE TermSizeSigHandler P((int sig));
191 char *InsertPxlSize P((char *pattern, int targetPxlSize));
192 XFontSet CreateFontSet P((char *base_fnt_lst));
194 char *FindFont P((char *pattern, int targetPxlSize));
196 void DelayedDrag P((void));
197 void ICSInputBoxPopUp P((void));
198 void MoveTypeInProc P((GdkEventKey *eventkey));
199 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
200 Boolean TempBackwardActive = False;
201 void DisplayMove P((int moveNumber));
202 void update_ics_width P(());
203 int CopyMemoProc P(());
204 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
208 XFontSet fontSet, clockFontSet;
211 XFontStruct *clockFontStruct;
213 Font coordFontID, countFontID;
214 XFontStruct *coordFontStruct, *countFontStruct;
216 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
217 GtkWidget *mainwindow;
219 Option *optList; // contains all widgets of main window
222 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
225 static GdkPixbuf *mainwindowIcon=NULL;
226 static GdkPixbuf *WhiteIcon=NULL;
227 static GdkPixbuf *BlackIcon=NULL;
229 /* key board accelerators */
230 GtkAccelGroup *GtkAccelerators;
232 typedef unsigned int BoardSize;
234 Boolean chessProgram;
236 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
237 int smallLayout = 0, tinyLayout = 0,
238 marginW, marginH, // [HGM] for run-time resizing
239 fromX = -1, fromY = -1, toX, toY, commentUp = False,
240 errorExitStatus = -1, defaultLineGap;
242 Dimension textHeight;
244 char *chessDir, *programName, *programVersion;
245 Boolean alwaysOnTop = False;
246 char *icsTextMenuString;
248 char *firstChessProgramNames;
249 char *secondChessProgramNames;
251 WindowPlacement wpMain;
252 WindowPlacement wpConsole;
253 WindowPlacement wpComment;
254 WindowPlacement wpMoveHistory;
255 WindowPlacement wpEvalGraph;
256 WindowPlacement wpEngineOutput;
257 WindowPlacement wpGameList;
258 WindowPlacement wpTags;
259 WindowPlacement wpDualBoard;
261 /* This magic number is the number of intermediate frames used
262 in each half of the animation. For short moves it's reduced
263 by 1. The total number of frames will be factor * 2 + 1. */
266 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
273 DropMenuEnables dmEnables[] = {
282 XtResource clientResources[] = {
283 { "flashCount", "flashCount", XtRInt, sizeof(int),
284 XtOffset(AppDataPtr, flashCount), XtRImmediate,
285 (XtPointer) FLASH_COUNT },
289 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
290 char globalTranslations[] =
291 ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
292 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
293 :<KeyDown>Return: TempBackwardProc() \n \
294 :<KeyUp>Return: TempForwardProc() \n";
296 char ICSInputTranslations[] =
297 "<Key>Up: UpKeyProc() \n "
298 "<Key>Down: DownKeyProc() \n "
299 "<Key>Return: EnterKeyProc() \n";
301 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
302 // as the widget is destroyed before the up-click can call extend-end
303 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
306 String xboardResources[] = {
307 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
315 gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
318 //---------------------------------------------------------------------------------------------------------
319 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
322 #define CW_USEDEFAULT (1<<31)
323 #define ICS_TEXT_MENU_SIZE 90
324 #define DEBUG_FILE "xboard.debug"
325 #define SetCurrentDirectory chdir
326 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
330 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
333 // front-end part of option handling
335 // [HGM] This platform-dependent table provides the location for storing the color info
336 extern char *crWhite, * crBlack;
340 &appData.whitePieceColor,
341 &appData.blackPieceColor,
342 &appData.lightSquareColor,
343 &appData.darkSquareColor,
344 &appData.highlightSquareColor,
345 &appData.premoveHighlightColor,
346 &appData.lowTimeWarningColor,
357 // [HGM] font: keep a font for each square size, even non-stndard ones
360 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
361 char *fontTable[NUM_FONTS][MAX_SIZE];
364 ParseFont (char *name, int number)
365 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
367 if(sscanf(name, "size%d:", &size)) {
368 // [HGM] font: font is meant for specific boardSize (likely from settings file);
369 // defer processing it until we know if it matches our board size
370 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
371 fontTable[number][size] = strdup(strchr(name, ':')+1);
372 fontValid[number][size] = True;
377 case 0: // CLOCK_FONT
378 appData.clockFont = strdup(name);
380 case 1: // MESSAGE_FONT
381 appData.font = strdup(name);
383 case 2: // COORD_FONT
384 appData.coordFont = strdup(name);
389 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
394 { // only 2 fonts currently
395 appData.clockFont = CLOCK_FONT_NAME;
396 appData.coordFont = COORD_FONT_NAME;
397 appData.font = DEFAULT_FONT_NAME;
402 { // no-op, until we identify the code for this already in XBoard and move it here
406 ParseColor (int n, char *name)
407 { // in XBoard, just copy the color-name string
408 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
412 ParseTextAttribs (ColorClass cc, char *s)
414 (&appData.colorShout)[cc] = strdup(s);
418 ParseBoardSize (void *addr, char *name)
420 appData.boardSize = strdup(name);
425 { // In XBoard the sound-playing program takes care of obtaining the actual sound
429 SetCommPortDefaults ()
430 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
433 // [HGM] args: these three cases taken out to stay in front-end
435 SaveFontArg (FILE *f, ArgDescriptor *ad)
438 int i, n = (int)(intptr_t)ad->argLoc;
440 case 0: // CLOCK_FONT
441 name = appData.clockFont;
443 case 1: // MESSAGE_FONT
446 case 2: // COORD_FONT
447 name = appData.coordFont;
452 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
453 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
454 fontTable[n][squareSize] = strdup(name);
455 fontValid[n][squareSize] = True;
458 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
459 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
464 { // nothing to do, as the sounds are at all times represented by their text-string names already
468 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
469 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
470 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
474 SaveColor (FILE *f, ArgDescriptor *ad)
475 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
476 if(colorVariable[(int)(intptr_t)ad->argLoc])
477 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
481 SaveBoardSize (FILE *f, char *name, void *addr)
482 { // wrapper to shield back-end from BoardSize & sizeInfo
483 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
487 ParseCommPortSettings (char *s)
488 { // no such option in XBoard (yet)
494 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
498 gtk_widget_get_allocation(shell, &a);
499 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
503 wp->height = a.height;
504 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
505 frameX = 3; frameY = 3; // remember to decide if windows touch
510 { // wrapper to shield use of window handles from back-end (make addressible by number?)
511 // In XBoard this will have to wait until awareness of window parameters is implemented
512 GetActualPlacement(shellWidget, &wpMain);
513 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
514 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
515 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
516 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
517 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
518 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
522 PrintCommPortSettings (FILE *f, char *name)
523 { // This option does not exist in XBoard
527 EnsureOnScreen (int *x, int *y, int minX, int minY)
534 { // [HGM] args: allows testing if main window is realized from back-end
535 return DialogExists(BoardWindow);
539 PopUpStartupDialog ()
540 { // start menu not implemented in XBoard
544 ConvertToLine (int argc, char **argv)
546 static char line[128*1024], buf[1024];
550 for(i=1; i<argc; i++)
552 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
553 && argv[i][0] != '{' )
554 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
556 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
557 strncat(line, buf, 128*1024 - strlen(line) - 1 );
560 line[strlen(line)-1] = NULLCHAR;
564 //--------------------------------------------------------------------------------------------
567 ResizeBoardWindow (int w, int h, int inhibit)
569 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
571 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
576 { // dummy, as the GTK code does not make colors in advance
581 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
582 { // determine what fonts to use, and create them
587 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
588 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
589 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
590 appData.font = fontTable[MESSAGE_FONT][squareSize];
591 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
592 appData.coordFont = fontTable[COORD_FONT][squareSize];
595 appData.font = InsertPxlSize(appData.font, fontPxlSize);
596 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
597 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
598 fontSet = CreateFontSet(appData.font);
599 clockFontSet = CreateFontSet(appData.clockFont);
601 /* For the coordFont, use the 0th font of the fontset. */
602 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
603 XFontStruct **font_struct_list;
604 XFontSetExtents *fontSize;
605 char **font_name_list;
606 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
607 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
608 coordFontStruct = XQueryFont(xDisplay, coordFontID);
609 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
610 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
613 appData.font = FindFont(appData.font, fontPxlSize);
614 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
615 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
616 clockFontID = XLoadFont(xDisplay, appData.clockFont);
617 clockFontStruct = XQueryFont(xDisplay, clockFontID);
618 coordFontID = XLoadFont(xDisplay, appData.coordFont);
619 coordFontStruct = XQueryFont(xDisplay, coordFontID);
620 // textHeight in !NLS mode!
622 countFontID = coordFontID; // [HGM] holdings
623 countFontStruct = coordFontStruct;
625 xdb = XtDatabase(xDisplay);
627 XrmPutLineResource(&xdb, "*international: True");
628 vTo.size = sizeof(XFontSet);
629 vTo.addr = (XtPointer) &fontSet;
630 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
632 XrmPutStringResource(&xdb, "*font", appData.font);
643 case ArgInt: p = " N"; break;
644 case ArgString: p = " STR"; break;
645 case ArgBoolean: p = " TF"; break;
646 case ArgSettingsFilename:
647 case ArgFilename: p = " FILE"; break;
648 case ArgX: p = " Nx"; break;
649 case ArgY: p = " Ny"; break;
650 case ArgAttribs: p = " TEXTCOL"; break;
651 case ArgColor: p = " COL"; break;
652 case ArgFont: p = " FONT"; break;
653 case ArgBoardSize: p = " SIZE"; break;
654 case ArgFloat: p = " FLOAT"; break;
659 case ArgCommSettings:
670 ArgDescriptor *q, *p = argDescriptors+5;
671 printf("\nXBoard accepts the following options:\n"
672 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
673 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
674 " SIZE = board-size spec(s)\n"
675 " Within parentheses are short forms, or options to set to true or false.\n"
676 " Persistent options (saved in the settings file) are marked with *)\n\n");
678 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
679 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
680 if(p->save) strcat(buf+len, "*");
681 for(q=p+1; q->argLoc == p->argLoc; q++) {
682 if(q->argName[0] == '-') continue;
683 strcat(buf+len, q == p+1 ? " (" : " ");
684 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
686 if(q != p+1) strcat(buf+len, ")");
688 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
691 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
695 main (int argc, char **argv)
697 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
698 int boardWidth, boardHeight, w, h;
700 int forceMono = False;
702 srandom(time(0)); // [HGM] book: make random truly random
704 setbuf(stdout, NULL);
705 setbuf(stderr, NULL);
708 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
709 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
713 if(argc > 1 && !strcmp(argv[1], "--help" )) {
719 gtk_init (&argc, &argv);
721 /* set up keyboard accelerators group */
722 GtkAccelerators = gtk_accel_group_new();
724 programName = strrchr(argv[0], '/');
725 if (programName == NULL)
726 programName = argv[0];
731 // if (appData.debugMode) {
732 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
735 bindtextdomain(PACKAGE, LOCALEDIR);
736 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
740 appData.boardSize = "";
741 InitAppData(ConvertToLine(argc, argv));
743 if (p == NULL) p = "/tmp";
744 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
745 gameCopyFilename = (char*) malloc(i);
746 gamePasteFilename = (char*) malloc(i);
747 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
748 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
750 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
751 static char buf[MSG_SIZ];
752 EscapeExpand(buf, appData.firstInitString);
753 appData.firstInitString = strdup(buf);
754 EscapeExpand(buf, appData.secondInitString);
755 appData.secondInitString = strdup(buf);
756 EscapeExpand(buf, appData.firstComputerString);
757 appData.firstComputerString = strdup(buf);
758 EscapeExpand(buf, appData.secondComputerString);
759 appData.secondComputerString = strdup(buf);
762 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
765 if (chdir(chessDir) != 0) {
766 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
772 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
773 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
774 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
775 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
778 setbuf(debugFP, NULL);
782 if (appData.debugMode) {
783 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
787 /* [HGM,HR] make sure board size is acceptable */
788 if(appData.NrFiles > BOARD_FILES ||
789 appData.NrRanks > BOARD_RANKS )
790 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
793 /* This feature does not work; animation needs a rewrite */
794 appData.highlightDragging = FALSE;
798 gameInfo.variant = StringToVariant(appData.variant);
802 * determine size, based on supplied or remembered -size, or screen size
804 if (isdigit(appData.boardSize[0])) {
805 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
806 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
807 &fontPxlSize, &smallLayout, &tinyLayout);
809 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
810 programName, appData.boardSize);
814 /* Find some defaults; use the nearest known size */
815 SizeDefaults *szd, *nearest;
816 int distance = 99999;
817 nearest = szd = sizeDefaults;
818 while (szd->name != NULL) {
819 if (abs(szd->squareSize - squareSize) < distance) {
821 distance = abs(szd->squareSize - squareSize);
822 if (distance == 0) break;
826 if (i < 2) lineGap = nearest->lineGap;
827 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
828 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
829 if (i < 5) fontPxlSize = nearest->fontPxlSize;
830 if (i < 6) smallLayout = nearest->smallLayout;
831 if (i < 7) tinyLayout = nearest->tinyLayout;
834 SizeDefaults *szd = sizeDefaults;
835 if (*appData.boardSize == NULLCHAR) {
836 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
837 guint screenwidth = gdk_screen_get_width(screen);
838 guint screenheight = gdk_screen_get_height(screen);
839 while (screenwidth < szd->minScreenSize ||
840 screenheight < szd->minScreenSize) {
843 if (szd->name == NULL) szd--;
844 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
846 while (szd->name != NULL &&
847 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
848 if (szd->name == NULL) {
849 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
850 programName, appData.boardSize);
854 squareSize = szd->squareSize;
855 lineGap = szd->lineGap;
856 clockFontPxlSize = szd->clockFontPxlSize;
857 coordFontPxlSize = szd->coordFontPxlSize;
858 fontPxlSize = szd->fontPxlSize;
859 smallLayout = szd->smallLayout;
860 tinyLayout = szd->tinyLayout;
861 // [HGM] font: use defaults from settings file if available and not overruled
864 defaultLineGap = lineGap;
865 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
867 /* [HR] height treated separately (hacked) */
868 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
869 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
872 * Determine what fonts to use.
875 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
879 * Detect if there are not enough colors available and adapt.
882 if (DefaultDepth(xDisplay, xScreen) <= 2) {
883 appData.monoMode = True;
887 forceMono = MakeColors();
890 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
892 appData.monoMode = True;
895 ParseIcsTextColors();
901 layoutName = "tinyLayout";
902 } else if (smallLayout) {
903 layoutName = "smallLayout";
905 layoutName = "normalLayout";
908 optList = BoardPopUp(squareSize, lineGap, (void*)
918 InitDrawingHandle(optList + W_BOARD);
919 shellWidget = shells[BoardWindow];
920 currBoard = &optList[W_BOARD];
921 boardWidget = optList[W_BOARD].handle;
922 menuBarWidget = optList[W_MENU].handle;
923 dropMenu = optList[W_DROP].handle;
924 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
926 formWidget = XtParent(boardWidget);
927 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
928 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
929 XtGetValues(optList[W_WHITE].handle, args, 2);
930 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
931 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
932 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
933 XtGetValues(optList[W_PAUSE].handle, args, 2);
937 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
938 // not need to go into InitDrawingSizes().
942 // add accelerators to main shell
943 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
946 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
948 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
949 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
950 mainwindowIcon = WhiteIcon;
951 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
955 * Create a cursor for the board widget.
958 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
959 XChangeWindowAttributes(xDisplay, xBoardWindow,
960 CWCursor, &window_attributes);
964 * Inhibit shell resizing.
967 shellArgs[0].value = (XtArgVal) &w;
968 shellArgs[1].value = (XtArgVal) &h;
969 XtGetValues(shellWidget, shellArgs, 2);
970 shellArgs[4].value = shellArgs[2].value = w;
971 shellArgs[5].value = shellArgs[3].value = h;
972 // XtSetValues(shellWidget, &shellArgs[2], 4);
976 gtk_widget_get_allocation(shells[BoardWindow], &a);
977 w = a.width; h = a.height;
978 //printf("start size (%d,%d), %dx%d\n", a.x, a.y, w, h);
979 gtk_widget_get_allocation(boardWidget, &a);
980 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
981 marginH = h - a.height + 13;
982 gtk_window_resize(GTK_WINDOW(shellWidget), marginW + boardWidth, marginH + boardHeight);
983 //printf("margins h=%d v=%d\n", marginW, marginH);
990 { // locate and read user logo
992 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
993 ASSIGN(userLogo, buf);
996 if (appData.animate || appData.animateDragging)
999 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1000 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1002 /* [AS] Restore layout */
1003 if( wpMoveHistory.visible ) {
1007 if( wpEvalGraph.visible )
1012 if( wpEngineOutput.visible ) {
1013 EngineOutputPopUp();
1018 if (errorExitStatus == -1) {
1019 if (appData.icsActive) {
1020 /* We now wait until we see "login:" from the ICS before
1021 sending the logon script (problems with timestamp otherwise) */
1022 /*ICSInitScript();*/
1023 if (appData.icsInputBox) ICSInputBoxPopUp();
1027 signal(SIGWINCH, TermSizeSigHandler);
1029 signal(SIGINT, IntSigHandler);
1030 signal(SIGTERM, IntSigHandler);
1031 if (*appData.cmailGameName != NULLCHAR) {
1032 signal(SIGUSR1, CmailSigHandler);
1036 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1039 // XtSetKeyboardFocus(shellWidget, formWidget);
1041 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1044 /* check for GTK events and process them */
1047 gtk_main_iteration();
1050 if (appData.debugMode) fclose(debugFP); // [DM] debug
1055 TermSizeSigHandler (int sig)
1061 IntSigHandler (int sig)
1067 CmailSigHandler (int sig)
1072 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1074 /* Activate call-back function CmailSigHandlerCallBack() */
1075 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1077 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1081 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1084 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1086 /**** end signal code ****/
1089 #define Abs(n) ((n)<0 ? -(n) : (n))
1093 InsertPxlSize (char *pattern, int targetPxlSize)
1095 char *base_fnt_lst, strInt[12], *p, *q;
1096 int alternatives, i, len, strIntLen;
1099 * Replace the "*" (if present) in the pixel-size slot of each
1100 * alternative with the targetPxlSize.
1104 while ((p = strchr(p, ',')) != NULL) {
1108 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1109 strIntLen = strlen(strInt);
1110 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1114 while (alternatives--) {
1115 char *comma = strchr(p, ',');
1116 for (i=0; i<14; i++) {
1117 char *hyphen = strchr(p, '-');
1119 if (comma && hyphen > comma) break;
1120 len = hyphen + 1 - p;
1121 if (i == 7 && *p == '*' && len == 2) {
1123 memcpy(q, strInt, strIntLen);
1133 len = comma + 1 - p;
1140 return base_fnt_lst;
1145 CreateFontSet (char *base_fnt_lst)
1148 char **missing_list;
1152 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1153 &missing_list, &missing_count, &def_string);
1154 if (appData.debugMode) {
1156 XFontStruct **font_struct_list;
1157 char **font_name_list;
1158 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1160 fprintf(debugFP, " got list %s, locale %s\n",
1161 XBaseFontNameListOfFontSet(fntSet),
1162 XLocaleOfFontSet(fntSet));
1163 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1164 for (i = 0; i < count; i++) {
1165 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1168 for (i = 0; i < missing_count; i++) {
1169 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1172 if (fntSet == NULL) {
1173 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1179 #else // not ENABLE_NLS
1181 * Find a font that matches "pattern" that is as close as
1182 * possible to the targetPxlSize. Prefer fonts that are k
1183 * pixels smaller to fonts that are k pixels larger. The
1184 * pattern must be in the X Consortium standard format,
1185 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1186 * The return value should be freed with XtFree when no
1190 FindFont (char *pattern, int targetPxlSize)
1192 char **fonts, *p, *best, *scalable, *scalableTail;
1193 int i, j, nfonts, minerr, err, pxlSize;
1196 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1198 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1199 programName, pattern);
1206 for (i=0; i<nfonts; i++) {
1209 if (*p != '-') continue;
1211 if (*p == NULLCHAR) break;
1212 if (*p++ == '-') j++;
1214 if (j < 7) continue;
1217 scalable = fonts[i];
1220 err = pxlSize - targetPxlSize;
1221 if (Abs(err) < Abs(minerr) ||
1222 (minerr > 0 && err < 0 && -err == minerr)) {
1228 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1229 /* If the error is too big and there is a scalable font,
1230 use the scalable font. */
1231 int headlen = scalableTail - scalable;
1232 p = (char *) XtMalloc(strlen(scalable) + 10);
1233 while (isdigit(*scalableTail)) scalableTail++;
1234 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1236 p = (char *) XtMalloc(strlen(best) + 2);
1237 safeStrCpy(p, best, strlen(best)+1 );
1239 if (appData.debugMode) {
1240 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1241 pattern, targetPxlSize, p);
1243 XFreeFontNames(fonts);
1250 EnableNamedMenuItem (char *menuRef, int state)
1252 MenuItem *item = MenuNameToItem(menuRef);
1254 if(item) gtk_widget_set_sensitive(item->handle, state);
1258 EnableButtonBar (int state)
1261 XtSetSensitive(optList[W_BUTTON].handle, state);
1267 SetMenuEnables (Enables *enab)
1269 while (enab->name != NULL) {
1270 EnableNamedMenuItem(enab->name, enab->value);
1275 gboolean KeyPressProc(window, eventkey, data)
1277 GdkEventKey *eventkey;
1281 MoveTypeInProc(eventkey); // pop up for typed in moves
1284 /* check for other key values */
1285 switch(eventkey->keyval) {
1297 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1298 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1300 if(*nprms == 0) return;
1301 item = MenuNameToItem(prms[0]);
1302 if(item) ((MenuProc *) item->proc) ();
1316 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1317 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1318 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1319 dmEnables[i].piece);
1320 XtSetSensitive(entry, p != NULL || !appData.testLegality
1321 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1322 && !appData.icsActive));
1324 while (p && *p++ == dmEnables[i].piece) count++;
1325 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1327 XtSetArg(args[j], XtNlabel, label); j++;
1328 XtSetValues(entry, args, j);
1334 do_flash_delay (unsigned long msec)
1340 FlashDelay (int flash_delay)
1342 if(flash_delay) do_flash_delay(flash_delay);
1346 Fraction (int x, int start, int stop)
1348 double f = ((double) x - start)/(stop - start);
1349 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1353 static WindowPlacement wpNew;
1356 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1358 int touch=0, fudge = 2, f = 2;
1359 GetActualPlacement(sh, wp);
1360 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1361 if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x) < fudge) touch = 2; else // left touch
1362 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1363 if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y) < fudge) touch = 4; // top touch
1364 //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);
1365 if(!touch ) return; // only windows that touch co-move
1366 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1367 int heightInc = wpNew.height - wpMain.height;
1368 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1369 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1370 wp->y += fracTop * heightInc;
1371 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1373 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1375 wp->height += heightInc;
1376 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1377 int widthInc = wpNew.width - wpMain.width;
1378 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1379 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1380 wp->y += fracLeft * widthInc;
1381 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1383 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1385 wp->width += widthInc;
1387 wp->x += wpNew.x - wpMain.x;
1388 wp->y += wpNew.y - wpMain.y;
1389 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1390 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1392 XtSetArg(args[j], XtNx, wp->x); j++;
1393 XtSetArg(args[j], XtNy, wp->y); j++;
1394 XtSetValues(sh, args, j);
1396 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1397 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1398 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1402 ReSize (WindowPlacement *wp)
1404 int sqx, sqy, w, h, lg = lineGap;
1405 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1406 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1407 sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1408 if(sqy < sqx) sqx = sqy;
1409 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1410 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1411 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1412 sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1413 if(sqy < sqx) sqx = sqy;
1415 if(sqx != squareSize) {
1416 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1417 squareSize = sqx; // adopt new square size
1418 CreatePNGPieces(); // make newly scaled pieces
1419 InitDrawingSizes(0, 0); // creates grid etc.
1420 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1421 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1422 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1423 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1424 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1427 static guint delayedDragTag = 0;
1436 // GetActualPlacement(shellWidget, &wpNew);
1437 //printf("drag proc (%d,%d) %dx%d\n", wpNew.x, wpNew.y, wpNew.width, wpNew.height);
1438 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1439 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1440 busy = 0; return; // false alarm
1443 if(appData.useStickyWindows) {
1444 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1445 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1446 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1447 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1450 DrawPosition(True, NULL);
1451 if(delayedDragTag) g_source_remove(delayedDragTag);
1452 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1459 //printf("old timr = %d\n", delayedDragTag);
1460 if(delayedDragTag) g_source_remove(delayedDragTag);
1461 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1462 //printf("new timr = %d\n", delayedDragTag);
1466 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1468 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1470 wpNew.x = event->configure.x;
1471 wpNew.y = event->configure.y;
1472 wpNew.width = event->configure.width;
1473 wpNew.height = event->configure.height;
1474 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1480 /* Disable all user input other than deleting the window */
1481 static int frozen = 0;
1487 /* Grab by a widget that doesn't accept input */
1488 gtk_grab_add(optList[W_MESSG].handle);
1492 /* Undo a FreezeUI */
1496 if (!frozen) return;
1497 gtk_grab_remove(optList[W_MESSG].handle);
1504 static int oldPausing = FALSE;
1505 static GameMode oldmode = (GameMode) -1;
1507 if (!boardWidget) return;
1509 if (pausing != oldPausing) {
1510 oldPausing = pausing;
1511 MarkMenuItem("Mode.Pause", pausing);
1513 if (appData.showButtonBar) {
1514 /* Always toggle, don't set. Previous code messes up when
1515 invoked while the button is pressed, as releasing it
1516 toggles the state again. */
1518 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1519 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1523 wname = ModeToWidgetName(oldmode);
1524 if (wname != NULL) {
1525 MarkMenuItem(wname, False);
1527 wname = ModeToWidgetName(gameMode);
1528 if (wname != NULL) {
1529 MarkMenuItem(wname, True);
1532 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1534 /* Maybe all the enables should be handled here, not just this one */
1535 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1537 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1542 * Button/menu procedures
1545 void CopyFileToClipboard(gchar *filename)
1547 gchar *selection_tmp;
1551 FILE* f = fopen(filename, "r");
1554 if (f == NULL) return;
1558 selection_tmp = g_try_malloc(len + 1);
1559 if (selection_tmp == NULL) {
1560 printf("Malloc failed in CopyFileToClipboard\n");
1563 count = fread(selection_tmp, 1, len, f);
1566 g_free(selection_tmp);
1569 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1571 // copy selection_tmp to clipboard
1572 GdkDisplay *gdisp = gdk_display_get_default();
1574 g_free(selection_tmp);
1577 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1578 gtk_clipboard_set_text(cb, selection_tmp, -1);
1579 g_free(selection_tmp);
1583 CopySomething (char *src)
1585 GdkDisplay *gdisp = gdk_display_get_default();
1587 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1588 if (gdisp == NULL) return;
1589 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1590 gtk_clipboard_set_text(cb, src, -1);
1594 PastePositionProc ()
1596 GdkDisplay *gdisp = gdk_display_get_default();
1600 if (gdisp == NULL) return;
1601 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1602 fenstr = gtk_clipboard_wait_for_text(cb);
1603 if (fenstr==NULL) return; // nothing had been selected to copy
1604 EditPositionPasteFEN(fenstr);
1616 // get game from clipboard
1617 GdkDisplay *gdisp = gdk_display_get_default();
1618 if (gdisp == NULL) return;
1619 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1620 text = gtk_clipboard_wait_for_text(cb);
1621 if (text == NULL) return; // nothing to paste
1624 // write to temp file
1625 if (text == NULL || len == 0) {
1626 return; //nothing to paste
1628 f = fopen(gamePasteFilename, "w");
1630 DisplayError(_("Can't open temp file"), errno);
1633 fwrite(text, 1, len, f);
1637 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1644 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1650 void MoveTypeInProc(eventkey)
1651 GdkEventKey *eventkey;
1655 // ingnore if ctrl or alt is pressed
1656 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
1660 buf[0]=eventkey->keyval;
1668 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1670 if (!TempBackwardActive) {
1671 TempBackwardActive = True;
1677 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1679 /* Check to see if triggered by a key release event for a repeating key.
1680 * If so the next queued event will be a key press of the same key at the same time */
1681 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1683 XPeekEvent(xDisplay, &next);
1684 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1685 next.xkey.keycode == event->xkey.keycode)
1689 TempBackwardActive = False;
1693 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1694 { // called as key binding
1697 if (nprms && *nprms > 0)
1701 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
1708 { // called from menu
1710 ManInner(NULL, NULL, NULL, NULL);
1715 SetWindowTitle (char *text, char *title, char *icon)
1720 if (appData.titleInWindow) {
1722 XtSetArg(args[i], XtNlabel, text); i++;
1723 XtSetValues(titleWidget, args, i);
1726 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1727 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1728 XtSetValues(shellWidget, args, i);
1729 XSync(xDisplay, False);
1731 if (appData.titleInWindow) {
1732 SetWidgetLabel(titleWidget, text);
1734 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1739 DisplayIcsInteractionTitle (String message)
1742 if (oldICSInteractionTitle == NULL) {
1743 /* Magic to find the old window title, adapted from vim */
1744 char *wina = getenv("WINDOWID");
1746 Window win = (Window) atoi(wina);
1747 Window root, parent, *children;
1748 unsigned int nchildren;
1749 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1751 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1752 if (!XQueryTree(xDisplay, win, &root, &parent,
1753 &children, &nchildren)) break;
1754 if (children) XFree((void *)children);
1755 if (parent == root || parent == 0) break;
1758 XSetErrorHandler(oldHandler);
1760 if (oldICSInteractionTitle == NULL) {
1761 oldICSInteractionTitle = "xterm";
1764 printf("\033]0;%s\007", message);
1771 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1773 GtkWidget *w = (GtkWidget *) opt->handle;
1780 strcpy(bgcolor, "black");
1781 strcpy(fgcolor, "white");
1783 strcpy(bgcolor, "white");
1784 strcpy(fgcolor, "black");
1787 appData.lowTimeWarning &&
1788 (timer / 1000) < appData.icsAlarmTime) {
1789 strcpy(fgcolor, appData.lowTimeWarningColor);
1792 gdk_color_parse( bgcolor, &col );
1793 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1795 if (appData.clockMode) {
1796 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1797 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1799 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
1800 bgcolor, fgcolor, color);
1802 gtk_label_set_markup(GTK_LABEL(w), markup);
1806 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1809 SetClockIcon (int color)
1811 GdkPixbuf *pm = *clockIcons[color];
1812 if (mainwindowIcon != pm) {
1813 mainwindowIcon = pm;
1814 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1818 #define INPUT_SOURCE_BUF_SIZE 8192
1827 char buf[INPUT_SOURCE_BUF_SIZE];
1832 DoInputCallback(io, cond, data)
1837 /* read input from one of the input source (for example a chess program, ICS, etc).
1838 * and call a function that will handle the input
1845 /* All information (callback function, file descriptor, etc) is
1846 * saved in an InputSource structure
1848 InputSource *is = (InputSource *) data;
1850 if (is->lineByLine) {
1851 count = read(is->fd, is->unused,
1852 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1854 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1857 is->unused += count;
1859 /* break input into lines and call the callback function on each
1862 while (p < is->unused) {
1863 q = memchr(p, '\n', is->unused - p);
1864 if (q == NULL) break;
1866 (is->func)(is, is->closure, p, q - p, 0);
1869 /* remember not yet used part of the buffer */
1871 while (p < is->unused) {
1876 /* read maximum length of input buffer and send the whole buffer
1877 * to the callback function
1879 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1884 (is->func)(is, is->closure, is->buf, count, error);
1886 return True; // Must return true or the watch will be removed
1889 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1896 GIOChannel *channel;
1897 ChildProc *cp = (ChildProc *) pr;
1899 is = (InputSource *) calloc(1, sizeof(InputSource));
1900 is->lineByLine = lineByLine;
1904 is->fd = fileno(stdin);
1906 is->kind = cp->kind;
1907 is->fd = cp->fdFrom;
1910 is->unused = is->buf;
1914 /* GTK-TODO: will this work on windows?*/
1916 channel = g_io_channel_unix_new(is->fd);
1917 g_io_channel_set_close_on_unref (channel, TRUE);
1918 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1920 is->closure = closure;
1921 return (InputSourceRef) is;
1926 RemoveInputSource(isr)
1929 InputSource *is = (InputSource *) isr;
1931 if (is->sid == 0) return;
1932 g_source_remove(is->sid);
1939 static Boolean frameWaiting;
1942 FrameAlarm (int sig)
1944 frameWaiting = False;
1945 /* In case System-V style signals. Needed?? */
1946 signal(SIGALRM, FrameAlarm);
1950 FrameDelay (int time)
1952 struct itimerval delay;
1955 frameWaiting = True;
1956 signal(SIGALRM, FrameAlarm);
1957 delay.it_interval.tv_sec =
1958 delay.it_value.tv_sec = time / 1000;
1959 delay.it_interval.tv_usec =
1960 delay.it_value.tv_usec = (time % 1000) * 1000;
1961 setitimer(ITIMER_REAL, &delay, NULL);
1962 while (frameWaiting) pause();
1963 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
1964 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
1965 setitimer(ITIMER_REAL, &delay, NULL);
1972 FrameDelay (int time)
1975 XSync(xDisplay, False);
1977 // gtk_main_iteration_do(False);
1980 usleep(time * 1000);
1986 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
1988 char buf[MSG_SIZ], *logoName = buf;
1989 if(appData.logo[n][0]) {
1990 logoName = appData.logo[n];
1991 } else if(appData.autoLogo) {
1992 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
1993 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
1994 } else if(appData.directory[n] && appData.directory[n][0]) {
1995 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
1999 { ASSIGN(cps->programLogo, logoName); }
2003 UpdateLogos (int displ)
2005 if(optList[W_WHITE-1].handle == NULL) return;
2006 LoadLogo(&first, 0, 0);
2007 LoadLogo(&second, 1, appData.icsActive);
2008 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2012 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2023 GtkFileFilter *gtkfilter;
2024 GtkFileFilter *gtkfilter_all;
2026 char fileext[10] = "";
2027 char *result = NULL;
2030 /* make a copy of the filter string, so that strtok can work with it*/
2031 cp = strndup(filter,strlen(filter));
2033 /* add filters for file extensions */
2034 gtkfilter = gtk_file_filter_new();
2035 gtkfilter_all = gtk_file_filter_new();
2037 /* one filter to show everything */
2038 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2039 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2041 /* add filter if present */
2042 result = strtok(cp, space);
2043 while( result != NULL ) {
2044 snprintf(fileext,10,"*%s",result);
2045 result = strtok( NULL, space );
2046 gtk_file_filter_add_pattern(gtkfilter, fileext);
2049 /* second filter to only show what's useful */
2050 gtk_file_filter_set_name (gtkfilter,filter);
2052 if (openMode[0] == 'r')
2054 dialog = gtk_file_chooser_dialog_new (label,
2056 GTK_FILE_CHOOSER_ACTION_OPEN,
2057 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2058 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2063 dialog = gtk_file_chooser_dialog_new (label,
2065 GTK_FILE_CHOOSER_ACTION_SAVE,
2066 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2067 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2069 /* add filename suggestions */
2070 if (strlen(def) > 0 )
2071 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2073 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2077 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2078 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2079 /* activate filter */
2080 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2082 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2087 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2090 f = fopen(filename, openMode);
2093 DisplayError(_("Failed to open file"), errno);
2097 /* TODO add indec */
2099 ASSIGN(*name, filename);
2100 ScheduleDelayedEvent(DelayedLoad, 50);
2105 gtk_widget_destroy (dialog);