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);
738 appData.boardSize = "";
739 InitAppData(ConvertToLine(argc, argv));
741 if (p == NULL) p = "/tmp";
742 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
743 gameCopyFilename = (char*) malloc(i);
744 gamePasteFilename = (char*) malloc(i);
745 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
746 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
748 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
749 static char buf[MSG_SIZ];
750 EscapeExpand(buf, appData.firstInitString);
751 appData.firstInitString = strdup(buf);
752 EscapeExpand(buf, appData.secondInitString);
753 appData.secondInitString = strdup(buf);
754 EscapeExpand(buf, appData.firstComputerString);
755 appData.firstComputerString = strdup(buf);
756 EscapeExpand(buf, appData.secondComputerString);
757 appData.secondComputerString = strdup(buf);
760 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
763 if (chdir(chessDir) != 0) {
764 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
770 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
771 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
772 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
773 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
776 setbuf(debugFP, NULL);
780 if (appData.debugMode) {
781 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
785 /* [HGM,HR] make sure board size is acceptable */
786 if(appData.NrFiles > BOARD_FILES ||
787 appData.NrRanks > BOARD_RANKS )
788 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
791 /* This feature does not work; animation needs a rewrite */
792 appData.highlightDragging = FALSE;
796 gameInfo.variant = StringToVariant(appData.variant);
800 * determine size, based on supplied or remembered -size, or screen size
802 if (isdigit(appData.boardSize[0])) {
803 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
804 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
805 &fontPxlSize, &smallLayout, &tinyLayout);
807 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
808 programName, appData.boardSize);
812 /* Find some defaults; use the nearest known size */
813 SizeDefaults *szd, *nearest;
814 int distance = 99999;
815 nearest = szd = sizeDefaults;
816 while (szd->name != NULL) {
817 if (abs(szd->squareSize - squareSize) < distance) {
819 distance = abs(szd->squareSize - squareSize);
820 if (distance == 0) break;
824 if (i < 2) lineGap = nearest->lineGap;
825 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
826 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
827 if (i < 5) fontPxlSize = nearest->fontPxlSize;
828 if (i < 6) smallLayout = nearest->smallLayout;
829 if (i < 7) tinyLayout = nearest->tinyLayout;
832 SizeDefaults *szd = sizeDefaults;
833 if (*appData.boardSize == NULLCHAR) {
834 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
835 guint screenwidth = gdk_screen_get_width(screen);
836 guint screenheight = gdk_screen_get_height(screen);
837 while (screenwidth < szd->minScreenSize ||
838 screenheight < szd->minScreenSize) {
841 if (szd->name == NULL) szd--;
842 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
844 while (szd->name != NULL &&
845 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
846 if (szd->name == NULL) {
847 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
848 programName, appData.boardSize);
852 squareSize = szd->squareSize;
853 lineGap = szd->lineGap;
854 clockFontPxlSize = szd->clockFontPxlSize;
855 coordFontPxlSize = szd->coordFontPxlSize;
856 fontPxlSize = szd->fontPxlSize;
857 smallLayout = szd->smallLayout;
858 tinyLayout = szd->tinyLayout;
859 // [HGM] font: use defaults from settings file if available and not overruled
862 defaultLineGap = lineGap;
863 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
865 /* [HR] height treated separately (hacked) */
866 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
867 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
870 * Determine what fonts to use.
873 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
877 * Detect if there are not enough colors available and adapt.
880 if (DefaultDepth(xDisplay, xScreen) <= 2) {
881 appData.monoMode = True;
885 forceMono = MakeColors();
888 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
890 appData.monoMode = True;
893 ParseIcsTextColors();
899 layoutName = "tinyLayout";
900 } else if (smallLayout) {
901 layoutName = "smallLayout";
903 layoutName = "normalLayout";
906 optList = BoardPopUp(squareSize, lineGap, (void*)
916 InitDrawingHandle(optList + W_BOARD);
917 shellWidget = shells[BoardWindow];
918 currBoard = &optList[W_BOARD];
919 boardWidget = optList[W_BOARD].handle;
920 menuBarWidget = optList[W_MENU].handle;
921 dropMenu = optList[W_DROP].handle;
922 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
924 formWidget = XtParent(boardWidget);
925 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
926 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
927 XtGetValues(optList[W_WHITE].handle, args, 2);
928 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
929 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
930 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
931 XtGetValues(optList[W_PAUSE].handle, args, 2);
935 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
936 // not need to go into InitDrawingSizes().
940 // add accelerators to main shell
941 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
944 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
946 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
947 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
948 mainwindowIcon = WhiteIcon;
949 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
953 * Create a cursor for the board widget.
956 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
957 XChangeWindowAttributes(xDisplay, xBoardWindow,
958 CWCursor, &window_attributes);
962 * Inhibit shell resizing.
965 shellArgs[0].value = (XtArgVal) &w;
966 shellArgs[1].value = (XtArgVal) &h;
967 XtGetValues(shellWidget, shellArgs, 2);
968 shellArgs[4].value = shellArgs[2].value = w;
969 shellArgs[5].value = shellArgs[3].value = h;
970 // XtSetValues(shellWidget, &shellArgs[2], 4);
974 gtk_widget_get_allocation(shells[BoardWindow], &a);
975 w = a.width; h = a.height;
976 //printf("start size (%d,%d), %dx%d\n", a.x, a.y, w, h);
977 gtk_widget_get_allocation(boardWidget, &a);
978 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
979 marginH = h - a.height + 13;
980 gtk_window_resize(GTK_WINDOW(shellWidget), marginW + boardWidth, marginH + boardHeight);
981 //printf("margins h=%d v=%d\n", marginW, marginH);
988 { // locate and read user logo
990 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
991 ASSIGN(userLogo, buf);
994 if (appData.animate || appData.animateDragging)
997 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
998 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1000 /* [AS] Restore layout */
1001 if( wpMoveHistory.visible ) {
1005 if( wpEvalGraph.visible )
1010 if( wpEngineOutput.visible ) {
1011 EngineOutputPopUp();
1016 if (errorExitStatus == -1) {
1017 if (appData.icsActive) {
1018 /* We now wait until we see "login:" from the ICS before
1019 sending the logon script (problems with timestamp otherwise) */
1020 /*ICSInitScript();*/
1021 if (appData.icsInputBox) ICSInputBoxPopUp();
1025 signal(SIGWINCH, TermSizeSigHandler);
1027 signal(SIGINT, IntSigHandler);
1028 signal(SIGTERM, IntSigHandler);
1029 if (*appData.cmailGameName != NULLCHAR) {
1030 signal(SIGUSR1, CmailSigHandler);
1034 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1037 // XtSetKeyboardFocus(shellWidget, formWidget);
1039 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1042 /* check for GTK events and process them */
1045 gtk_main_iteration();
1048 if (appData.debugMode) fclose(debugFP); // [DM] debug
1053 TermSizeSigHandler (int sig)
1059 IntSigHandler (int sig)
1065 CmailSigHandler (int sig)
1070 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1072 /* Activate call-back function CmailSigHandlerCallBack() */
1073 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1075 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1079 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1082 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1084 /**** end signal code ****/
1087 #define Abs(n) ((n)<0 ? -(n) : (n))
1091 InsertPxlSize (char *pattern, int targetPxlSize)
1093 char *base_fnt_lst, strInt[12], *p, *q;
1094 int alternatives, i, len, strIntLen;
1097 * Replace the "*" (if present) in the pixel-size slot of each
1098 * alternative with the targetPxlSize.
1102 while ((p = strchr(p, ',')) != NULL) {
1106 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1107 strIntLen = strlen(strInt);
1108 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1112 while (alternatives--) {
1113 char *comma = strchr(p, ',');
1114 for (i=0; i<14; i++) {
1115 char *hyphen = strchr(p, '-');
1117 if (comma && hyphen > comma) break;
1118 len = hyphen + 1 - p;
1119 if (i == 7 && *p == '*' && len == 2) {
1121 memcpy(q, strInt, strIntLen);
1131 len = comma + 1 - p;
1138 return base_fnt_lst;
1143 CreateFontSet (char *base_fnt_lst)
1146 char **missing_list;
1150 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1151 &missing_list, &missing_count, &def_string);
1152 if (appData.debugMode) {
1154 XFontStruct **font_struct_list;
1155 char **font_name_list;
1156 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1158 fprintf(debugFP, " got list %s, locale %s\n",
1159 XBaseFontNameListOfFontSet(fntSet),
1160 XLocaleOfFontSet(fntSet));
1161 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1162 for (i = 0; i < count; i++) {
1163 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1166 for (i = 0; i < missing_count; i++) {
1167 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1170 if (fntSet == NULL) {
1171 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1177 #else // not ENABLE_NLS
1179 * Find a font that matches "pattern" that is as close as
1180 * possible to the targetPxlSize. Prefer fonts that are k
1181 * pixels smaller to fonts that are k pixels larger. The
1182 * pattern must be in the X Consortium standard format,
1183 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1184 * The return value should be freed with XtFree when no
1188 FindFont (char *pattern, int targetPxlSize)
1190 char **fonts, *p, *best, *scalable, *scalableTail;
1191 int i, j, nfonts, minerr, err, pxlSize;
1194 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1196 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1197 programName, pattern);
1204 for (i=0; i<nfonts; i++) {
1207 if (*p != '-') continue;
1209 if (*p == NULLCHAR) break;
1210 if (*p++ == '-') j++;
1212 if (j < 7) continue;
1215 scalable = fonts[i];
1218 err = pxlSize - targetPxlSize;
1219 if (Abs(err) < Abs(minerr) ||
1220 (minerr > 0 && err < 0 && -err == minerr)) {
1226 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1227 /* If the error is too big and there is a scalable font,
1228 use the scalable font. */
1229 int headlen = scalableTail - scalable;
1230 p = (char *) XtMalloc(strlen(scalable) + 10);
1231 while (isdigit(*scalableTail)) scalableTail++;
1232 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1234 p = (char *) XtMalloc(strlen(best) + 2);
1235 safeStrCpy(p, best, strlen(best)+1 );
1237 if (appData.debugMode) {
1238 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1239 pattern, targetPxlSize, p);
1241 XFreeFontNames(fonts);
1248 EnableNamedMenuItem (char *menuRef, int state)
1250 MenuItem *item = MenuNameToItem(menuRef);
1252 if(item) gtk_widget_set_sensitive(item->handle, state);
1256 EnableButtonBar (int state)
1259 XtSetSensitive(optList[W_BUTTON].handle, state);
1265 SetMenuEnables (Enables *enab)
1267 while (enab->name != NULL) {
1268 EnableNamedMenuItem(enab->name, enab->value);
1273 gboolean KeyPressProc(window, eventkey, data)
1275 GdkEventKey *eventkey;
1279 MoveTypeInProc(eventkey); // pop up for typed in moves
1282 /* check for other key values */
1283 switch(eventkey->keyval) {
1295 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1296 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1298 if(*nprms == 0) return;
1299 item = MenuNameToItem(prms[0]);
1300 if(item) ((MenuProc *) item->proc) ();
1314 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1315 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1316 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1317 dmEnables[i].piece);
1318 XtSetSensitive(entry, p != NULL || !appData.testLegality
1319 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1320 && !appData.icsActive));
1322 while (p && *p++ == dmEnables[i].piece) count++;
1323 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1325 XtSetArg(args[j], XtNlabel, label); j++;
1326 XtSetValues(entry, args, j);
1332 do_flash_delay (unsigned long msec)
1338 FlashDelay (int flash_delay)
1340 if(flash_delay) do_flash_delay(flash_delay);
1344 Fraction (int x, int start, int stop)
1346 double f = ((double) x - start)/(stop - start);
1347 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1351 static WindowPlacement wpNew;
1354 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1356 int touch=0, fudge = 2, f = 2;
1357 GetActualPlacement(sh, wp);
1358 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1359 if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x) < fudge) touch = 2; else // left touch
1360 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1361 if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y) < fudge) touch = 4; // top touch
1362 //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);
1363 if(!touch ) return; // only windows that touch co-move
1364 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1365 int heightInc = wpNew.height - wpMain.height;
1366 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1367 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1368 wp->y += fracTop * heightInc;
1369 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1371 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1373 wp->height += heightInc;
1374 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1375 int widthInc = wpNew.width - wpMain.width;
1376 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1377 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1378 wp->y += fracLeft * widthInc;
1379 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1381 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1383 wp->width += widthInc;
1385 wp->x += wpNew.x - wpMain.x;
1386 wp->y += wpNew.y - wpMain.y;
1387 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1388 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1390 XtSetArg(args[j], XtNx, wp->x); j++;
1391 XtSetArg(args[j], XtNy, wp->y); j++;
1392 XtSetValues(sh, args, j);
1394 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1395 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1396 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1400 ReSize (WindowPlacement *wp)
1402 int sqx, sqy, w, h, lg = lineGap;
1403 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1404 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1405 sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1406 if(sqy < sqx) sqx = sqy;
1407 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1408 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1409 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1410 sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1411 if(sqy < sqx) sqx = sqy;
1413 if(sqx != squareSize) {
1414 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1415 squareSize = sqx; // adopt new square size
1416 CreatePNGPieces(); // make newly scaled pieces
1417 InitDrawingSizes(0, 0); // creates grid etc.
1418 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1419 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1420 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1421 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1422 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1425 static guint delayedDragTag = 0;
1434 // GetActualPlacement(shellWidget, &wpNew);
1435 //printf("drag proc (%d,%d) %dx%d\n", wpNew.x, wpNew.y, wpNew.width, wpNew.height);
1436 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1437 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1438 busy = 0; return; // false alarm
1441 if(appData.useStickyWindows) {
1442 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1443 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1444 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1445 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1448 DrawPosition(True, NULL);
1449 if(delayedDragTag) g_source_remove(delayedDragTag);
1450 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1457 //printf("old timr = %d\n", delayedDragTag);
1458 if(delayedDragTag) g_source_remove(delayedDragTag);
1459 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1460 //printf("new timr = %d\n", delayedDragTag);
1464 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1466 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1468 wpNew.x = event->configure.x;
1469 wpNew.y = event->configure.y;
1470 wpNew.width = event->configure.width;
1471 wpNew.height = event->configure.height;
1472 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1478 /* Disable all user input other than deleting the window */
1479 static int frozen = 0;
1485 /* Grab by a widget that doesn't accept input */
1486 gtk_grab_add(optList[W_MESSG].handle);
1490 /* Undo a FreezeUI */
1494 if (!frozen) return;
1495 gtk_grab_remove(optList[W_MESSG].handle);
1502 static int oldPausing = FALSE;
1503 static GameMode oldmode = (GameMode) -1;
1505 if (!boardWidget) return;
1507 if (pausing != oldPausing) {
1508 oldPausing = pausing;
1509 MarkMenuItem("Mode.Pause", pausing);
1511 if (appData.showButtonBar) {
1512 /* Always toggle, don't set. Previous code messes up when
1513 invoked while the button is pressed, as releasing it
1514 toggles the state again. */
1516 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1517 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1521 wname = ModeToWidgetName(oldmode);
1522 if (wname != NULL) {
1523 MarkMenuItem(wname, False);
1525 wname = ModeToWidgetName(gameMode);
1526 if (wname != NULL) {
1527 MarkMenuItem(wname, True);
1530 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1532 /* Maybe all the enables should be handled here, not just this one */
1533 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1535 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1540 * Button/menu procedures
1543 void CopyFileToClipboard(gchar *filename)
1545 gchar *selection_tmp;
1549 FILE* f = fopen(filename, "r");
1552 if (f == NULL) return;
1556 selection_tmp = g_try_malloc(len + 1);
1557 if (selection_tmp == NULL) {
1558 printf("Malloc failed in CopyFileToClipboard\n");
1561 count = fread(selection_tmp, 1, len, f);
1564 g_free(selection_tmp);
1567 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1569 // copy selection_tmp to clipboard
1570 GdkDisplay *gdisp = gdk_display_get_default();
1572 g_free(selection_tmp);
1575 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1576 gtk_clipboard_set_text(cb, selection_tmp, -1);
1577 g_free(selection_tmp);
1581 CopySomething (char *src)
1583 GdkDisplay *gdisp = gdk_display_get_default();
1585 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1586 if (gdisp == NULL) return;
1587 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1588 gtk_clipboard_set_text(cb, src, -1);
1592 PastePositionProc ()
1594 GdkDisplay *gdisp = gdk_display_get_default();
1598 if (gdisp == NULL) return;
1599 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1600 fenstr = gtk_clipboard_wait_for_text(cb);
1601 if (fenstr==NULL) return; // nothing had been selected to copy
1602 EditPositionPasteFEN(fenstr);
1614 // get game from clipboard
1615 GdkDisplay *gdisp = gdk_display_get_default();
1616 if (gdisp == NULL) return;
1617 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1618 text = gtk_clipboard_wait_for_text(cb);
1619 if (text == NULL) return; // nothing to paste
1622 // write to temp file
1623 if (text == NULL || len == 0) {
1624 return; //nothing to paste
1626 f = fopen(gamePasteFilename, "w");
1628 DisplayError(_("Can't open temp file"), errno);
1631 fwrite(text, 1, len, f);
1635 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1642 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1648 void MoveTypeInProc(eventkey)
1649 GdkEventKey *eventkey;
1653 // ingnore if ctrl or alt is pressed
1654 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
1658 buf[0]=eventkey->keyval;
1666 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1668 if (!TempBackwardActive) {
1669 TempBackwardActive = True;
1675 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1677 /* Check to see if triggered by a key release event for a repeating key.
1678 * If so the next queued event will be a key press of the same key at the same time */
1679 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1681 XPeekEvent(xDisplay, &next);
1682 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1683 next.xkey.keycode == event->xkey.keycode)
1687 TempBackwardActive = False;
1691 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1692 { // called as key binding
1695 if (nprms && *nprms > 0)
1699 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
1706 { // called from menu
1708 ManInner(NULL, NULL, NULL, NULL);
1713 SetWindowTitle (char *text, char *title, char *icon)
1718 if (appData.titleInWindow) {
1720 XtSetArg(args[i], XtNlabel, text); i++;
1721 XtSetValues(titleWidget, args, i);
1724 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1725 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1726 XtSetValues(shellWidget, args, i);
1727 XSync(xDisplay, False);
1729 if (appData.titleInWindow) {
1730 SetWidgetLabel(titleWidget, text);
1732 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1737 DisplayIcsInteractionTitle (String message)
1740 if (oldICSInteractionTitle == NULL) {
1741 /* Magic to find the old window title, adapted from vim */
1742 char *wina = getenv("WINDOWID");
1744 Window win = (Window) atoi(wina);
1745 Window root, parent, *children;
1746 unsigned int nchildren;
1747 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1749 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1750 if (!XQueryTree(xDisplay, win, &root, &parent,
1751 &children, &nchildren)) break;
1752 if (children) XFree((void *)children);
1753 if (parent == root || parent == 0) break;
1756 XSetErrorHandler(oldHandler);
1758 if (oldICSInteractionTitle == NULL) {
1759 oldICSInteractionTitle = "xterm";
1762 printf("\033]0;%s\007", message);
1769 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1771 GtkWidget *w = (GtkWidget *) opt->handle;
1778 strcpy(bgcolor, "black");
1779 strcpy(fgcolor, "white");
1781 strcpy(bgcolor, "white");
1782 strcpy(fgcolor, "black");
1785 appData.lowTimeWarning &&
1786 (timer / 1000) < appData.icsAlarmTime) {
1787 strcpy(fgcolor, appData.lowTimeWarningColor);
1790 gdk_color_parse( bgcolor, &col );
1791 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1793 if (appData.clockMode) {
1794 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1795 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1797 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
1798 bgcolor, fgcolor, color);
1800 gtk_label_set_markup(GTK_LABEL(w), markup);
1804 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1807 SetClockIcon (int color)
1809 GdkPixbuf *pm = *clockIcons[color];
1810 if (mainwindowIcon != pm) {
1811 mainwindowIcon = pm;
1812 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1816 #define INPUT_SOURCE_BUF_SIZE 8192
1825 char buf[INPUT_SOURCE_BUF_SIZE];
1830 DoInputCallback(io, cond, data)
1835 /* read input from one of the input source (for example a chess program, ICS, etc).
1836 * and call a function that will handle the input
1843 /* All information (callback function, file descriptor, etc) is
1844 * saved in an InputSource structure
1846 InputSource *is = (InputSource *) data;
1848 if (is->lineByLine) {
1849 count = read(is->fd, is->unused,
1850 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1852 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1855 is->unused += count;
1857 /* break input into lines and call the callback function on each
1860 while (p < is->unused) {
1861 q = memchr(p, '\n', is->unused - p);
1862 if (q == NULL) break;
1864 (is->func)(is, is->closure, p, q - p, 0);
1867 /* remember not yet used part of the buffer */
1869 while (p < is->unused) {
1874 /* read maximum length of input buffer and send the whole buffer
1875 * to the callback function
1877 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1882 (is->func)(is, is->closure, is->buf, count, error);
1884 return True; // Must return true or the watch will be removed
1887 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1894 GIOChannel *channel;
1895 ChildProc *cp = (ChildProc *) pr;
1897 is = (InputSource *) calloc(1, sizeof(InputSource));
1898 is->lineByLine = lineByLine;
1902 is->fd = fileno(stdin);
1904 is->kind = cp->kind;
1905 is->fd = cp->fdFrom;
1908 is->unused = is->buf;
1912 /* GTK-TODO: will this work on windows?*/
1914 channel = g_io_channel_unix_new(is->fd);
1915 g_io_channel_set_close_on_unref (channel, TRUE);
1916 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1918 is->closure = closure;
1919 return (InputSourceRef) is;
1924 RemoveInputSource(isr)
1927 InputSource *is = (InputSource *) isr;
1929 if (is->sid == 0) return;
1930 g_source_remove(is->sid);
1937 static Boolean frameWaiting;
1940 FrameAlarm (int sig)
1942 frameWaiting = False;
1943 /* In case System-V style signals. Needed?? */
1944 signal(SIGALRM, FrameAlarm);
1948 FrameDelay (int time)
1950 struct itimerval delay;
1953 frameWaiting = True;
1954 signal(SIGALRM, FrameAlarm);
1955 delay.it_interval.tv_sec =
1956 delay.it_value.tv_sec = time / 1000;
1957 delay.it_interval.tv_usec =
1958 delay.it_value.tv_usec = (time % 1000) * 1000;
1959 setitimer(ITIMER_REAL, &delay, NULL);
1960 while (frameWaiting) pause();
1961 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
1962 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
1963 setitimer(ITIMER_REAL, &delay, NULL);
1970 FrameDelay (int time)
1973 XSync(xDisplay, False);
1975 // gtk_main_iteration_do(False);
1978 usleep(time * 1000);
1984 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
1986 char buf[MSG_SIZ], *logoName = buf;
1987 if(appData.logo[n][0]) {
1988 logoName = appData.logo[n];
1989 } else if(appData.autoLogo) {
1990 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
1991 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
1992 } else if(appData.directory[n] && appData.directory[n][0]) {
1993 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
1997 { ASSIGN(cps->programLogo, logoName); }
2001 UpdateLogos (int displ)
2003 if(optList[W_WHITE-1].handle == NULL) return;
2004 LoadLogo(&first, 0, 0);
2005 LoadLogo(&second, 1, appData.icsActive);
2006 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2010 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2021 GtkFileFilter *gtkfilter;
2022 GtkFileFilter *gtkfilter_all;
2024 char fileext[10] = "";
2025 char *result = NULL;
2028 /* make a copy of the filter string, so that strtok can work with it*/
2029 cp = strndup(filter,strlen(filter));
2031 /* add filters for file extensions */
2032 gtkfilter = gtk_file_filter_new();
2033 gtkfilter_all = gtk_file_filter_new();
2035 /* one filter to show everything */
2036 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2037 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2039 /* add filter if present */
2040 result = strtok(cp, space);
2041 while( result != NULL ) {
2042 snprintf(fileext,10,"*%s",result);
2043 result = strtok( NULL, space );
2044 gtk_file_filter_add_pattern(gtkfilter, fileext);
2047 /* second filter to only show what's useful */
2048 gtk_file_filter_set_name (gtkfilter,filter);
2050 if (openMode[0] == 'r')
2052 dialog = gtk_file_chooser_dialog_new (label,
2054 GTK_FILE_CHOOSER_ACTION_OPEN,
2055 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2056 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2061 dialog = gtk_file_chooser_dialog_new (label,
2063 GTK_FILE_CHOOSER_ACTION_SAVE,
2064 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2065 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2067 /* add filename suggestions */
2068 if (strlen(def) > 0 )
2069 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2071 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2075 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2076 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2077 /* activate filter */
2078 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2080 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2085 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2088 f = fopen(filename, openMode);
2091 DisplayError(_("Failed to open file"), errno);
2095 /* TODO add indec */
2097 ASSIGN(*name, filename);
2098 ScheduleDelayedEvent(DelayedLoad, 50);
2103 gtk_widget_destroy (dialog);