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;
260 /* This magic number is the number of intermediate frames used
261 in each half of the animation. For short moves it's reduced
262 by 1. The total number of frames will be factor * 2 + 1. */
265 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
272 DropMenuEnables dmEnables[] = {
281 XtResource clientResources[] = {
282 { "flashCount", "flashCount", XtRInt, sizeof(int),
283 XtOffset(AppDataPtr, flashCount), XtRImmediate,
284 (XtPointer) FLASH_COUNT },
288 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
289 char globalTranslations[] =
290 ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
291 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
292 :<KeyDown>Return: TempBackwardProc() \n \
293 :<KeyUp>Return: TempForwardProc() \n";
295 char ICSInputTranslations[] =
296 "<Key>Up: UpKeyProc() \n "
297 "<Key>Down: DownKeyProc() \n "
298 "<Key>Return: EnterKeyProc() \n";
300 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
301 // as the widget is destroyed before the up-click can call extend-end
302 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
305 String xboardResources[] = {
306 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
314 gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
317 //---------------------------------------------------------------------------------------------------------
318 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
321 #define CW_USEDEFAULT (1<<31)
322 #define ICS_TEXT_MENU_SIZE 90
323 #define DEBUG_FILE "xboard.debug"
324 #define SetCurrentDirectory chdir
325 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
329 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
332 // front-end part of option handling
334 // [HGM] This platform-dependent table provides the location for storing the color info
335 extern char *crWhite, * crBlack;
339 &appData.whitePieceColor,
340 &appData.blackPieceColor,
341 &appData.lightSquareColor,
342 &appData.darkSquareColor,
343 &appData.highlightSquareColor,
344 &appData.premoveHighlightColor,
345 &appData.lowTimeWarningColor,
356 // [HGM] font: keep a font for each square size, even non-stndard ones
359 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
360 char *fontTable[NUM_FONTS][MAX_SIZE];
363 ParseFont (char *name, int number)
364 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
366 if(sscanf(name, "size%d:", &size)) {
367 // [HGM] font: font is meant for specific boardSize (likely from settings file);
368 // defer processing it until we know if it matches our board size
369 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
370 fontTable[number][size] = strdup(strchr(name, ':')+1);
371 fontValid[number][size] = True;
376 case 0: // CLOCK_FONT
377 appData.clockFont = strdup(name);
379 case 1: // MESSAGE_FONT
380 appData.font = strdup(name);
382 case 2: // COORD_FONT
383 appData.coordFont = strdup(name);
388 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
393 { // only 2 fonts currently
394 appData.clockFont = CLOCK_FONT_NAME;
395 appData.coordFont = COORD_FONT_NAME;
396 appData.font = DEFAULT_FONT_NAME;
401 { // no-op, until we identify the code for this already in XBoard and move it here
405 ParseColor (int n, char *name)
406 { // in XBoard, just copy the color-name string
407 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
411 ParseTextAttribs (ColorClass cc, char *s)
413 (&appData.colorShout)[cc] = strdup(s);
417 ParseBoardSize (void *addr, char *name)
419 appData.boardSize = strdup(name);
424 { // In XBoard the sound-playing program takes care of obtaining the actual sound
428 SetCommPortDefaults ()
429 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
432 // [HGM] args: these three cases taken out to stay in front-end
434 SaveFontArg (FILE *f, ArgDescriptor *ad)
437 int i, n = (int)(intptr_t)ad->argLoc;
439 case 0: // CLOCK_FONT
440 name = appData.clockFont;
442 case 1: // MESSAGE_FONT
445 case 2: // COORD_FONT
446 name = appData.coordFont;
451 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
452 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
453 fontTable[n][squareSize] = strdup(name);
454 fontValid[n][squareSize] = True;
457 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
458 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
463 { // nothing to do, as the sounds are at all times represented by their text-string names already
467 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
468 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
469 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
473 SaveColor (FILE *f, ArgDescriptor *ad)
474 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
475 if(colorVariable[(int)(intptr_t)ad->argLoc])
476 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
480 SaveBoardSize (FILE *f, char *name, void *addr)
481 { // wrapper to shield back-end from BoardSize & sizeInfo
482 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
486 ParseCommPortSettings (char *s)
487 { // no such option in XBoard (yet)
493 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
497 gtk_widget_get_allocation(shell, &a);
498 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
502 wp->height = a.height;
503 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
504 frameX = 3; frameY = 3; // remember to decide if windows touch
509 { // wrapper to shield use of window handles from back-end (make addressible by number?)
510 // In XBoard this will have to wait until awareness of window parameters is implemented
511 GetActualPlacement(shellWidget, &wpMain);
512 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
513 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
514 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
515 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
516 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
517 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
521 PrintCommPortSettings (FILE *f, char *name)
522 { // This option does not exist in XBoard
526 EnsureOnScreen (int *x, int *y, int minX, int minY)
533 { // [HGM] args: allows testing if main window is realized from back-end
534 return DialogExists(BoardWindow);
538 PopUpStartupDialog ()
539 { // start menu not implemented in XBoard
543 ConvertToLine (int argc, char **argv)
545 static char line[128*1024], buf[1024];
549 for(i=1; i<argc; i++)
551 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
552 && argv[i][0] != '{' )
553 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
555 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
556 strncat(line, buf, 128*1024 - strlen(line) - 1 );
559 line[strlen(line)-1] = NULLCHAR;
563 //--------------------------------------------------------------------------------------------
566 ResizeBoardWindow (int w, int h, int inhibit)
568 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
570 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
575 { // dummy, as the GTK code does not make colors in advance
580 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
581 { // determine what fonts to use, and create them
586 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
587 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
588 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
589 appData.font = fontTable[MESSAGE_FONT][squareSize];
590 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
591 appData.coordFont = fontTable[COORD_FONT][squareSize];
594 appData.font = InsertPxlSize(appData.font, fontPxlSize);
595 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
596 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
597 fontSet = CreateFontSet(appData.font);
598 clockFontSet = CreateFontSet(appData.clockFont);
600 /* For the coordFont, use the 0th font of the fontset. */
601 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
602 XFontStruct **font_struct_list;
603 XFontSetExtents *fontSize;
604 char **font_name_list;
605 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
606 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
607 coordFontStruct = XQueryFont(xDisplay, coordFontID);
608 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
609 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
612 appData.font = FindFont(appData.font, fontPxlSize);
613 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
614 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
615 clockFontID = XLoadFont(xDisplay, appData.clockFont);
616 clockFontStruct = XQueryFont(xDisplay, clockFontID);
617 coordFontID = XLoadFont(xDisplay, appData.coordFont);
618 coordFontStruct = XQueryFont(xDisplay, coordFontID);
619 // textHeight in !NLS mode!
621 countFontID = coordFontID; // [HGM] holdings
622 countFontStruct = coordFontStruct;
624 xdb = XtDatabase(xDisplay);
626 XrmPutLineResource(&xdb, "*international: True");
627 vTo.size = sizeof(XFontSet);
628 vTo.addr = (XtPointer) &fontSet;
629 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
631 XrmPutStringResource(&xdb, "*font", appData.font);
642 case ArgInt: p = " N"; break;
643 case ArgString: p = " STR"; break;
644 case ArgBoolean: p = " TF"; break;
645 case ArgSettingsFilename:
646 case ArgFilename: p = " FILE"; break;
647 case ArgX: p = " Nx"; break;
648 case ArgY: p = " Ny"; break;
649 case ArgAttribs: p = " TEXTCOL"; break;
650 case ArgColor: p = " COL"; break;
651 case ArgFont: p = " FONT"; break;
652 case ArgBoardSize: p = " SIZE"; break;
653 case ArgFloat: p = " FLOAT"; break;
658 case ArgCommSettings:
669 ArgDescriptor *q, *p = argDescriptors+5;
670 printf("\nXBoard accepts the following options:\n"
671 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
672 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
673 " SIZE = board-size spec(s)\n"
674 " Within parentheses are short forms, or options to set to true or false.\n"
675 " Persistent options (saved in the settings file) are marked with *)\n\n");
677 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
678 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
679 if(p->save) strcat(buf+len, "*");
680 for(q=p+1; q->argLoc == p->argLoc; q++) {
681 if(q->argName[0] == '-') continue;
682 strcat(buf+len, q == p+1 ? " (" : " ");
683 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
685 if(q != p+1) strcat(buf+len, ")");
687 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
690 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
694 main (int argc, char **argv)
696 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
697 int boardWidth, boardHeight, w, h;
699 int forceMono = False;
701 srandom(time(0)); // [HGM] book: make random truly random
703 setbuf(stdout, NULL);
704 setbuf(stderr, NULL);
707 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
708 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
712 if(argc > 1 && !strcmp(argv[1], "--help" )) {
718 gtk_init (&argc, &argv);
720 /* set up keyboard accelerators group */
721 GtkAccelerators = gtk_accel_group_new();
723 programName = strrchr(argv[0], '/');
724 if (programName == NULL)
725 programName = argv[0];
730 // if (appData.debugMode) {
731 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
734 bindtextdomain(PACKAGE, LOCALEDIR);
735 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
739 appData.boardSize = "";
740 InitAppData(ConvertToLine(argc, argv));
742 if (p == NULL) p = "/tmp";
743 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
744 gameCopyFilename = (char*) malloc(i);
745 gamePasteFilename = (char*) malloc(i);
746 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
747 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
749 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
750 static char buf[MSG_SIZ];
751 EscapeExpand(buf, appData.firstInitString);
752 appData.firstInitString = strdup(buf);
753 EscapeExpand(buf, appData.secondInitString);
754 appData.secondInitString = strdup(buf);
755 EscapeExpand(buf, appData.firstComputerString);
756 appData.firstComputerString = strdup(buf);
757 EscapeExpand(buf, appData.secondComputerString);
758 appData.secondComputerString = strdup(buf);
761 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
764 if (chdir(chessDir) != 0) {
765 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
771 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
772 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
773 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
774 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
777 setbuf(debugFP, NULL);
781 if (appData.debugMode) {
782 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
786 /* [HGM,HR] make sure board size is acceptable */
787 if(appData.NrFiles > BOARD_FILES ||
788 appData.NrRanks > BOARD_RANKS )
789 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
792 /* This feature does not work; animation needs a rewrite */
793 appData.highlightDragging = FALSE;
797 gameInfo.variant = StringToVariant(appData.variant);
801 * determine size, based on supplied or remembered -size, or screen size
803 if (isdigit(appData.boardSize[0])) {
804 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
805 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
806 &fontPxlSize, &smallLayout, &tinyLayout);
808 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
809 programName, appData.boardSize);
813 /* Find some defaults; use the nearest known size */
814 SizeDefaults *szd, *nearest;
815 int distance = 99999;
816 nearest = szd = sizeDefaults;
817 while (szd->name != NULL) {
818 if (abs(szd->squareSize - squareSize) < distance) {
820 distance = abs(szd->squareSize - squareSize);
821 if (distance == 0) break;
825 if (i < 2) lineGap = nearest->lineGap;
826 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
827 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
828 if (i < 5) fontPxlSize = nearest->fontPxlSize;
829 if (i < 6) smallLayout = nearest->smallLayout;
830 if (i < 7) tinyLayout = nearest->tinyLayout;
833 SizeDefaults *szd = sizeDefaults;
834 if (*appData.boardSize == NULLCHAR) {
835 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
836 guint screenwidth = gdk_screen_get_width(screen);
837 guint screenheight = gdk_screen_get_height(screen);
838 while (screenwidth < szd->minScreenSize ||
839 screenheight < szd->minScreenSize) {
842 if (szd->name == NULL) szd--;
843 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
845 while (szd->name != NULL &&
846 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
847 if (szd->name == NULL) {
848 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
849 programName, appData.boardSize);
853 squareSize = szd->squareSize;
854 lineGap = szd->lineGap;
855 clockFontPxlSize = szd->clockFontPxlSize;
856 coordFontPxlSize = szd->coordFontPxlSize;
857 fontPxlSize = szd->fontPxlSize;
858 smallLayout = szd->smallLayout;
859 tinyLayout = szd->tinyLayout;
860 // [HGM] font: use defaults from settings file if available and not overruled
863 defaultLineGap = lineGap;
864 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
866 /* [HR] height treated separately (hacked) */
867 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
868 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
871 * Determine what fonts to use.
874 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
878 * Detect if there are not enough colors available and adapt.
881 if (DefaultDepth(xDisplay, xScreen) <= 2) {
882 appData.monoMode = True;
886 forceMono = MakeColors();
889 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
891 appData.monoMode = True;
894 ParseIcsTextColors();
900 layoutName = "tinyLayout";
901 } else if (smallLayout) {
902 layoutName = "smallLayout";
904 layoutName = "normalLayout";
907 optList = BoardPopUp(squareSize, lineGap, (void*)
917 InitDrawingHandle(optList + W_BOARD);
918 shellWidget = shells[BoardWindow];
919 currBoard = &optList[W_BOARD];
920 boardWidget = optList[W_BOARD].handle;
921 menuBarWidget = optList[W_MENU].handle;
922 dropMenu = optList[W_DROP].handle;
923 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
925 formWidget = XtParent(boardWidget);
926 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
927 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
928 XtGetValues(optList[W_WHITE].handle, args, 2);
929 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
930 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
931 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
932 XtGetValues(optList[W_PAUSE].handle, args, 2);
936 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
937 // not need to go into InitDrawingSizes().
941 // add accelerators to main shell
942 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
945 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
947 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
948 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
949 mainwindowIcon = WhiteIcon;
950 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
954 * Create a cursor for the board widget.
957 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
958 XChangeWindowAttributes(xDisplay, xBoardWindow,
959 CWCursor, &window_attributes);
963 * Inhibit shell resizing.
966 shellArgs[0].value = (XtArgVal) &w;
967 shellArgs[1].value = (XtArgVal) &h;
968 XtGetValues(shellWidget, shellArgs, 2);
969 shellArgs[4].value = shellArgs[2].value = w;
970 shellArgs[5].value = shellArgs[3].value = h;
971 // XtSetValues(shellWidget, &shellArgs[2], 4);
975 gtk_widget_get_allocation(shells[BoardWindow], &a);
976 w = a.width; h = a.height;
977 //printf("start size (%d,%d), %dx%d\n", a.x, a.y, w, h);
978 gtk_widget_get_allocation(boardWidget, &a);
979 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
980 marginH = h - a.height + 13;
981 gtk_window_resize(GTK_WINDOW(shellWidget), marginW + boardWidth, marginH + boardHeight);
982 //printf("margins h=%d v=%d\n", marginW, marginH);
989 { // locate and read user logo
991 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
992 ASSIGN(userLogo, buf);
995 if (appData.animate || appData.animateDragging)
998 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
999 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1001 /* [AS] Restore layout */
1002 if( wpMoveHistory.visible ) {
1006 if( wpEvalGraph.visible )
1011 if( wpEngineOutput.visible ) {
1012 EngineOutputPopUp();
1017 if (errorExitStatus == -1) {
1018 if (appData.icsActive) {
1019 /* We now wait until we see "login:" from the ICS before
1020 sending the logon script (problems with timestamp otherwise) */
1021 /*ICSInitScript();*/
1022 if (appData.icsInputBox) ICSInputBoxPopUp();
1026 signal(SIGWINCH, TermSizeSigHandler);
1028 signal(SIGINT, IntSigHandler);
1029 signal(SIGTERM, IntSigHandler);
1030 if (*appData.cmailGameName != NULLCHAR) {
1031 signal(SIGUSR1, CmailSigHandler);
1035 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1038 // XtSetKeyboardFocus(shellWidget, formWidget);
1040 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1043 /* check for GTK events and process them */
1046 gtk_main_iteration();
1049 if (appData.debugMode) fclose(debugFP); // [DM] debug
1054 TermSizeSigHandler (int sig)
1060 IntSigHandler (int sig)
1066 CmailSigHandler (int sig)
1071 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1073 /* Activate call-back function CmailSigHandlerCallBack() */
1074 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1076 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1080 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1083 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1085 /**** end signal code ****/
1088 #define Abs(n) ((n)<0 ? -(n) : (n))
1092 InsertPxlSize (char *pattern, int targetPxlSize)
1094 char *base_fnt_lst, strInt[12], *p, *q;
1095 int alternatives, i, len, strIntLen;
1098 * Replace the "*" (if present) in the pixel-size slot of each
1099 * alternative with the targetPxlSize.
1103 while ((p = strchr(p, ',')) != NULL) {
1107 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1108 strIntLen = strlen(strInt);
1109 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1113 while (alternatives--) {
1114 char *comma = strchr(p, ',');
1115 for (i=0; i<14; i++) {
1116 char *hyphen = strchr(p, '-');
1118 if (comma && hyphen > comma) break;
1119 len = hyphen + 1 - p;
1120 if (i == 7 && *p == '*' && len == 2) {
1122 memcpy(q, strInt, strIntLen);
1132 len = comma + 1 - p;
1139 return base_fnt_lst;
1144 CreateFontSet (char *base_fnt_lst)
1147 char **missing_list;
1151 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1152 &missing_list, &missing_count, &def_string);
1153 if (appData.debugMode) {
1155 XFontStruct **font_struct_list;
1156 char **font_name_list;
1157 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1159 fprintf(debugFP, " got list %s, locale %s\n",
1160 XBaseFontNameListOfFontSet(fntSet),
1161 XLocaleOfFontSet(fntSet));
1162 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1163 for (i = 0; i < count; i++) {
1164 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1167 for (i = 0; i < missing_count; i++) {
1168 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1171 if (fntSet == NULL) {
1172 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1178 #else // not ENABLE_NLS
1180 * Find a font that matches "pattern" that is as close as
1181 * possible to the targetPxlSize. Prefer fonts that are k
1182 * pixels smaller to fonts that are k pixels larger. The
1183 * pattern must be in the X Consortium standard format,
1184 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1185 * The return value should be freed with XtFree when no
1189 FindFont (char *pattern, int targetPxlSize)
1191 char **fonts, *p, *best, *scalable, *scalableTail;
1192 int i, j, nfonts, minerr, err, pxlSize;
1195 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1197 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1198 programName, pattern);
1205 for (i=0; i<nfonts; i++) {
1208 if (*p != '-') continue;
1210 if (*p == NULLCHAR) break;
1211 if (*p++ == '-') j++;
1213 if (j < 7) continue;
1216 scalable = fonts[i];
1219 err = pxlSize - targetPxlSize;
1220 if (Abs(err) < Abs(minerr) ||
1221 (minerr > 0 && err < 0 && -err == minerr)) {
1227 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1228 /* If the error is too big and there is a scalable font,
1229 use the scalable font. */
1230 int headlen = scalableTail - scalable;
1231 p = (char *) XtMalloc(strlen(scalable) + 10);
1232 while (isdigit(*scalableTail)) scalableTail++;
1233 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1235 p = (char *) XtMalloc(strlen(best) + 2);
1236 safeStrCpy(p, best, strlen(best)+1 );
1238 if (appData.debugMode) {
1239 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1240 pattern, targetPxlSize, p);
1242 XFreeFontNames(fonts);
1249 EnableNamedMenuItem (char *menuRef, int state)
1251 MenuItem *item = MenuNameToItem(menuRef);
1253 if(item) gtk_widget_set_sensitive(item->handle, state);
1257 EnableButtonBar (int state)
1260 XtSetSensitive(optList[W_BUTTON].handle, state);
1266 SetMenuEnables (Enables *enab)
1268 while (enab->name != NULL) {
1269 EnableNamedMenuItem(enab->name, enab->value);
1274 gboolean KeyPressProc(window, eventkey, data)
1276 GdkEventKey *eventkey;
1280 MoveTypeInProc(eventkey); // pop up for typed in moves
1283 /* check for other key values */
1284 switch(eventkey->keyval) {
1296 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1297 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1299 if(*nprms == 0) return;
1300 item = MenuNameToItem(prms[0]);
1301 if(item) ((MenuProc *) item->proc) ();
1315 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1316 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1317 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1318 dmEnables[i].piece);
1319 XtSetSensitive(entry, p != NULL || !appData.testLegality
1320 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1321 && !appData.icsActive));
1323 while (p && *p++ == dmEnables[i].piece) count++;
1324 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1326 XtSetArg(args[j], XtNlabel, label); j++;
1327 XtSetValues(entry, args, j);
1333 do_flash_delay (unsigned long msec)
1339 FlashDelay (int flash_delay)
1341 if(flash_delay) do_flash_delay(flash_delay);
1345 Fraction (int x, int start, int stop)
1347 double f = ((double) x - start)/(stop - start);
1348 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1352 static WindowPlacement wpNew;
1355 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1357 int touch=0, fudge = 2, f = 2;
1358 GetActualPlacement(sh, wp);
1359 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1360 if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x) < fudge) touch = 2; else // left touch
1361 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1362 if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y) < fudge) touch = 4; // top touch
1363 //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);
1364 if(!touch ) return; // only windows that touch co-move
1365 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1366 int heightInc = wpNew.height - wpMain.height;
1367 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1368 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1369 wp->y += fracTop * heightInc;
1370 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1372 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1374 wp->height += heightInc;
1375 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1376 int widthInc = wpNew.width - wpMain.width;
1377 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1378 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1379 wp->y += fracLeft * widthInc;
1380 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1382 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1384 wp->width += widthInc;
1386 wp->x += wpNew.x - wpMain.x;
1387 wp->y += wpNew.y - wpMain.y;
1388 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1389 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1391 XtSetArg(args[j], XtNx, wp->x); j++;
1392 XtSetArg(args[j], XtNy, wp->y); j++;
1393 XtSetValues(sh, args, j);
1395 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1396 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1397 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1401 ReSize (WindowPlacement *wp)
1403 int sqx, sqy, w, h, lg = lineGap;
1404 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1405 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1406 sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1407 if(sqy < sqx) sqx = sqy;
1408 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1409 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1410 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1411 sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1412 if(sqy < sqx) sqx = sqy;
1414 if(sqx != squareSize) {
1415 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1416 squareSize = sqx; // adopt new square size
1417 CreatePNGPieces(); // make newly scaled pieces
1418 InitDrawingSizes(0, 0); // creates grid etc.
1419 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1420 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1421 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1422 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1423 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1426 static guint delayedDragTag = 0;
1435 // GetActualPlacement(shellWidget, &wpNew);
1436 //printf("drag proc (%d,%d) %dx%d\n", wpNew.x, wpNew.y, wpNew.width, wpNew.height);
1437 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1438 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1439 busy = 0; return; // false alarm
1442 if(appData.useStickyWindows) {
1443 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1444 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1445 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1446 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1449 DrawPosition(True, NULL);
1450 if(delayedDragTag) g_source_remove(delayedDragTag);
1451 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1458 //printf("old timr = %d\n", delayedDragTag);
1459 if(delayedDragTag) g_source_remove(delayedDragTag);
1460 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1461 //printf("new timr = %d\n", delayedDragTag);
1465 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1467 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1469 wpNew.x = event->configure.x;
1470 wpNew.y = event->configure.y;
1471 wpNew.width = event->configure.width;
1472 wpNew.height = event->configure.height;
1473 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1479 /* Disable all user input other than deleting the window */
1480 static int frozen = 0;
1486 /* Grab by a widget that doesn't accept input */
1487 gtk_grab_add(optList[W_MESSG].handle);
1491 /* Undo a FreezeUI */
1495 if (!frozen) return;
1496 gtk_grab_remove(optList[W_MESSG].handle);
1503 static int oldPausing = FALSE;
1504 static GameMode oldmode = (GameMode) -1;
1506 if (!boardWidget) return;
1508 if (pausing != oldPausing) {
1509 oldPausing = pausing;
1510 MarkMenuItem("Mode.Pause", pausing);
1512 if (appData.showButtonBar) {
1513 /* Always toggle, don't set. Previous code messes up when
1514 invoked while the button is pressed, as releasing it
1515 toggles the state again. */
1517 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1518 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1522 wname = ModeToWidgetName(oldmode);
1523 if (wname != NULL) {
1524 MarkMenuItem(wname, False);
1526 wname = ModeToWidgetName(gameMode);
1527 if (wname != NULL) {
1528 MarkMenuItem(wname, True);
1531 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1533 /* Maybe all the enables should be handled here, not just this one */
1534 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1536 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1541 * Button/menu procedures
1544 void CopyFileToClipboard(gchar *filename)
1546 gchar *selection_tmp;
1550 FILE* f = fopen(filename, "r");
1553 if (f == NULL) return;
1557 selection_tmp = g_try_malloc(len + 1);
1558 if (selection_tmp == NULL) {
1559 printf("Malloc failed in CopyFileToClipboard\n");
1562 count = fread(selection_tmp, 1, len, f);
1565 g_free(selection_tmp);
1568 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1570 // copy selection_tmp to clipboard
1571 GdkDisplay *gdisp = gdk_display_get_default();
1573 g_free(selection_tmp);
1576 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1577 gtk_clipboard_set_text(cb, selection_tmp, -1);
1578 g_free(selection_tmp);
1582 CopySomething (char *src)
1584 GdkDisplay *gdisp = gdk_display_get_default();
1586 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1587 if (gdisp == NULL) return;
1588 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1589 gtk_clipboard_set_text(cb, src, -1);
1593 PastePositionProc ()
1595 GdkDisplay *gdisp = gdk_display_get_default();
1599 if (gdisp == NULL) return;
1600 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1601 fenstr = gtk_clipboard_wait_for_text(cb);
1602 if (fenstr==NULL) return; // nothing had been selected to copy
1603 EditPositionPasteFEN(fenstr);
1615 // get game from clipboard
1616 GdkDisplay *gdisp = gdk_display_get_default();
1617 if (gdisp == NULL) return;
1618 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1619 text = gtk_clipboard_wait_for_text(cb);
1620 if (text == NULL) return; // nothing to paste
1623 // write to temp file
1624 if (text == NULL || len == 0) {
1625 return; //nothing to paste
1627 f = fopen(gamePasteFilename, "w");
1629 DisplayError(_("Can't open temp file"), errno);
1632 fwrite(text, 1, len, f);
1636 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1643 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1649 void MoveTypeInProc(eventkey)
1650 GdkEventKey *eventkey;
1654 // ingnore if ctrl or alt is pressed
1655 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
1659 buf[0]=eventkey->keyval;
1667 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1669 if (!TempBackwardActive) {
1670 TempBackwardActive = True;
1676 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1678 /* Check to see if triggered by a key release event for a repeating key.
1679 * If so the next queued event will be a key press of the same key at the same time */
1680 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1682 XPeekEvent(xDisplay, &next);
1683 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1684 next.xkey.keycode == event->xkey.keycode)
1688 TempBackwardActive = False;
1692 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1693 { // called as key binding
1696 if (nprms && *nprms > 0)
1700 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
1707 { // called from menu
1709 ManInner(NULL, NULL, NULL, NULL);
1714 SetWindowTitle (char *text, char *title, char *icon)
1719 if (appData.titleInWindow) {
1721 XtSetArg(args[i], XtNlabel, text); i++;
1722 XtSetValues(titleWidget, args, i);
1725 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1726 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1727 XtSetValues(shellWidget, args, i);
1728 XSync(xDisplay, False);
1730 if (appData.titleInWindow) {
1731 SetWidgetLabel(titleWidget, text);
1733 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1738 DisplayIcsInteractionTitle (String message)
1741 if (oldICSInteractionTitle == NULL) {
1742 /* Magic to find the old window title, adapted from vim */
1743 char *wina = getenv("WINDOWID");
1745 Window win = (Window) atoi(wina);
1746 Window root, parent, *children;
1747 unsigned int nchildren;
1748 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1750 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1751 if (!XQueryTree(xDisplay, win, &root, &parent,
1752 &children, &nchildren)) break;
1753 if (children) XFree((void *)children);
1754 if (parent == root || parent == 0) break;
1757 XSetErrorHandler(oldHandler);
1759 if (oldICSInteractionTitle == NULL) {
1760 oldICSInteractionTitle = "xterm";
1763 printf("\033]0;%s\007", message);
1770 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1772 GtkWidget *w = (GtkWidget *) opt->handle;
1779 strcpy(bgcolor, "black");
1780 strcpy(fgcolor, "white");
1782 strcpy(bgcolor, "white");
1783 strcpy(fgcolor, "black");
1786 appData.lowTimeWarning &&
1787 (timer / 1000) < appData.icsAlarmTime) {
1788 strcpy(fgcolor, appData.lowTimeWarningColor);
1791 gdk_color_parse( bgcolor, &col );
1792 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1794 if (appData.clockMode) {
1795 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1796 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1798 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
1799 bgcolor, fgcolor, color);
1801 gtk_label_set_markup(GTK_LABEL(w), markup);
1805 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1808 SetClockIcon (int color)
1810 GdkPixbuf *pm = *clockIcons[color];
1811 if (mainwindowIcon != pm) {
1812 mainwindowIcon = pm;
1813 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1817 #define INPUT_SOURCE_BUF_SIZE 8192
1826 char buf[INPUT_SOURCE_BUF_SIZE];
1831 DoInputCallback(io, cond, data)
1836 /* read input from one of the input source (for example a chess program, ICS, etc).
1837 * and call a function that will handle the input
1844 /* All information (callback function, file descriptor, etc) is
1845 * saved in an InputSource structure
1847 InputSource *is = (InputSource *) data;
1849 if (is->lineByLine) {
1850 count = read(is->fd, is->unused,
1851 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1853 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1856 is->unused += count;
1858 /* break input into lines and call the callback function on each
1861 while (p < is->unused) {
1862 q = memchr(p, '\n', is->unused - p);
1863 if (q == NULL) break;
1865 (is->func)(is, is->closure, p, q - p, 0);
1868 /* remember not yet used part of the buffer */
1870 while (p < is->unused) {
1875 /* read maximum length of input buffer and send the whole buffer
1876 * to the callback function
1878 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1883 (is->func)(is, is->closure, is->buf, count, error);
1885 return True; // Must return true or the watch will be removed
1888 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1895 GIOChannel *channel;
1896 ChildProc *cp = (ChildProc *) pr;
1898 is = (InputSource *) calloc(1, sizeof(InputSource));
1899 is->lineByLine = lineByLine;
1903 is->fd = fileno(stdin);
1905 is->kind = cp->kind;
1906 is->fd = cp->fdFrom;
1909 is->unused = is->buf;
1913 /* GTK-TODO: will this work on windows?*/
1915 channel = g_io_channel_unix_new(is->fd);
1916 g_io_channel_set_close_on_unref (channel, TRUE);
1917 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1919 is->closure = closure;
1920 return (InputSourceRef) is;
1925 RemoveInputSource(isr)
1928 InputSource *is = (InputSource *) isr;
1930 if (is->sid == 0) return;
1931 g_source_remove(is->sid);
1938 static Boolean frameWaiting;
1941 FrameAlarm (int sig)
1943 frameWaiting = False;
1944 /* In case System-V style signals. Needed?? */
1945 signal(SIGALRM, FrameAlarm);
1949 FrameDelay (int time)
1951 struct itimerval delay;
1954 frameWaiting = True;
1955 signal(SIGALRM, FrameAlarm);
1956 delay.it_interval.tv_sec =
1957 delay.it_value.tv_sec = time / 1000;
1958 delay.it_interval.tv_usec =
1959 delay.it_value.tv_usec = (time % 1000) * 1000;
1960 setitimer(ITIMER_REAL, &delay, NULL);
1961 while (frameWaiting) pause();
1962 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
1963 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
1964 setitimer(ITIMER_REAL, &delay, NULL);
1971 FrameDelay (int time)
1974 XSync(xDisplay, False);
1976 // gtk_main_iteration_do(False);
1979 usleep(time * 1000);
1985 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
1987 char buf[MSG_SIZ], *logoName = buf;
1988 if(appData.logo[n][0]) {
1989 logoName = appData.logo[n];
1990 } else if(appData.autoLogo) {
1991 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
1992 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
1993 } else if(appData.directory[n] && appData.directory[n][0]) {
1994 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
1998 { ASSIGN(cps->programLogo, logoName); }
2002 UpdateLogos (int displ)
2004 if(optList[W_WHITE-1].handle == NULL) return;
2005 LoadLogo(&first, 0, 0);
2006 LoadLogo(&second, 1, appData.icsActive);
2007 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2011 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2022 GtkFileFilter *gtkfilter;
2023 GtkFileFilter *gtkfilter_all;
2025 char fileext[10] = "";
2026 char *result = NULL;
2029 /* make a copy of the filter string, so that strtok can work with it*/
2030 cp = strndup(filter,strlen(filter));
2032 /* add filters for file extensions */
2033 gtkfilter = gtk_file_filter_new();
2034 gtkfilter_all = gtk_file_filter_new();
2036 /* one filter to show everything */
2037 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2038 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2040 /* add filter if present */
2041 result = strtok(cp, space);
2042 while( result != NULL ) {
2043 snprintf(fileext,10,"*%s",result);
2044 result = strtok( NULL, space );
2045 gtk_file_filter_add_pattern(gtkfilter, fileext);
2048 /* second filter to only show what's useful */
2049 gtk_file_filter_set_name (gtkfilter,filter);
2051 if (openMode[0] == 'r')
2053 dialog = gtk_file_chooser_dialog_new (label,
2055 GTK_FILE_CHOOSER_ACTION_OPEN,
2056 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2057 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2062 dialog = gtk_file_chooser_dialog_new (label,
2064 GTK_FILE_CHOOSER_ACTION_SAVE,
2065 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2066 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2068 /* add filename suggestions */
2069 if (strlen(def) > 0 )
2070 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2072 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2076 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2077 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2078 /* activate filter */
2079 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2081 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2086 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2089 f = fopen(filename, openMode);
2092 DisplayError(_("Failed to open file"), errno);
2096 /* TODO add indec */
2098 ASSIGN(*name, filename);
2099 ScheduleDelayedEvent(DelayedLoad, 50);
2104 gtk_widget_destroy (dialog);