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 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 ICSInitScript P((void));
203 void update_ics_width P(());
204 int CopyMemoProc P(());
205 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
209 XFontSet fontSet, clockFontSet;
212 XFontStruct *clockFontStruct;
214 Font coordFontID, countFontID;
215 XFontStruct *coordFontStruct, *countFontStruct;
217 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
218 GtkWidget *mainwindow;
220 Option *optList; // contains all widgets of main window
223 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
226 static GdkPixbuf *mainwindowIcon=NULL;
227 static GdkPixbuf *WhiteIcon=NULL;
228 static GdkPixbuf *BlackIcon=NULL;
230 typedef unsigned int BoardSize;
232 Boolean chessProgram;
234 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
235 int smallLayout = 0, tinyLayout = 0,
236 marginW, marginH, // [HGM] for run-time resizing
237 fromX = -1, fromY = -1, toX, toY, commentUp = False,
238 errorExitStatus = -1, defaultLineGap;
240 Dimension textHeight;
242 char *chessDir, *programName, *programVersion;
243 Boolean alwaysOnTop = False;
244 char *icsTextMenuString;
246 char *firstChessProgramNames;
247 char *secondChessProgramNames;
249 WindowPlacement wpMain;
250 WindowPlacement wpConsole;
251 WindowPlacement wpComment;
252 WindowPlacement wpMoveHistory;
253 WindowPlacement wpEvalGraph;
254 WindowPlacement wpEngineOutput;
255 WindowPlacement wpGameList;
256 WindowPlacement wpTags;
258 /* This magic number is the number of intermediate frames used
259 in each half of the animation. For short moves it's reduced
260 by 1. The total number of frames will be factor * 2 + 1. */
263 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
270 DropMenuEnables dmEnables[] = {
279 XtResource clientResources[] = {
280 { "flashCount", "flashCount", XtRInt, sizeof(int),
281 XtOffset(AppDataPtr, flashCount), XtRImmediate,
282 (XtPointer) FLASH_COUNT },
286 char globalTranslations[] =
287 ":<Key>F9: MenuItem(Actions.Resign) \n \
288 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
289 :Meta<Key>V: MenuItem(File.NewVariant) \n \
290 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
291 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
292 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
293 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
294 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
295 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
296 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
297 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
298 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
299 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
300 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
301 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
302 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
303 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
304 :Ctrl<Key>q: MenuItem(File.Quit) \n \
305 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
306 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
307 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
308 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
309 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
310 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
311 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
312 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
313 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
314 :Meta<Key>G: MenuItem(View.GameList) \n \
315 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
316 :<Key>Pause: MenuItem(Mode.Pause) \n \
317 :<Key>F3: MenuItem(Action.Accept) \n \
318 :<Key>F4: MenuItem(Action.Decline) \n \
319 :<Key>F12: MenuItem(Action.Rematch) \n \
320 :<Key>F5: MenuItem(Action.CallFlag) \n \
321 :<Key>F6: MenuItem(Action.Draw) \n \
322 :<Key>F7: MenuItem(Action.Adjourn) \n \
323 :<Key>F8: MenuItem(Action.Abort) \n \
324 :<Key>F10: MenuItem(Action.StopObserving) \n \
325 :<Key>F11: MenuItem(Action.StopExamining) \n \
326 :Ctrl<Key>d: MenuItem(DebugProc) \n \
327 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
328 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
329 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
330 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
331 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
332 :<Key>Left: MenuItem(Edit.Backward) \n \
333 :<Key>Right: MenuItem(Edit.Forward) \n \
334 :<Key>Home: MenuItem(Edit.Revert) \n \
335 :<Key>End: MenuItem(Edit.TruncateGame) \n \
336 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
337 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
338 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
339 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
340 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
341 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
342 #ifndef OPTIONSDIALOG
344 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
345 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
346 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
347 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
348 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
351 :<Key>F1: MenuItem(Help.ManXBoard) \n \
352 :<Key>F2: MenuItem(View.FlipView) \n \
353 :<KeyDown>Return: TempBackwardProc() \n \
354 :<KeyUp>Return: TempForwardProc() \n";
356 char ICSInputTranslations[] =
357 "<Key>Up: UpKeyProc() \n "
358 "<Key>Down: DownKeyProc() \n "
359 "<Key>Return: EnterKeyProc() \n";
361 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
362 // as the widget is destroyed before the up-click can call extend-end
363 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
366 String xboardResources[] = {
367 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
375 gtk_window_present(GTK_WINDOW(mainwindow));
378 //---------------------------------------------------------------------------------------------------------
379 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
382 #define CW_USEDEFAULT (1<<31)
383 #define ICS_TEXT_MENU_SIZE 90
384 #define DEBUG_FILE "xboard.debug"
385 #define SetCurrentDirectory chdir
386 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
390 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
393 // front-end part of option handling
395 // [HGM] This platform-dependent table provides the location for storing the color info
396 extern char *crWhite, * crBlack;
400 &appData.whitePieceColor,
401 &appData.blackPieceColor,
402 &appData.lightSquareColor,
403 &appData.darkSquareColor,
404 &appData.highlightSquareColor,
405 &appData.premoveHighlightColor,
406 &appData.lowTimeWarningColor,
417 // [HGM] font: keep a font for each square size, even non-stndard ones
420 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
421 char *fontTable[NUM_FONTS][MAX_SIZE];
424 ParseFont (char *name, int number)
425 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
427 if(sscanf(name, "size%d:", &size)) {
428 // [HGM] font: font is meant for specific boardSize (likely from settings file);
429 // defer processing it until we know if it matches our board size
430 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
431 fontTable[number][size] = strdup(strchr(name, ':')+1);
432 fontValid[number][size] = True;
437 case 0: // CLOCK_FONT
438 appData.clockFont = strdup(name);
440 case 1: // MESSAGE_FONT
441 appData.font = strdup(name);
443 case 2: // COORD_FONT
444 appData.coordFont = strdup(name);
449 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
454 { // only 2 fonts currently
455 appData.clockFont = CLOCK_FONT_NAME;
456 appData.coordFont = COORD_FONT_NAME;
457 appData.font = DEFAULT_FONT_NAME;
462 { // no-op, until we identify the code for this already in XBoard and move it here
466 ParseColor (int n, char *name)
467 { // in XBoard, just copy the color-name string
468 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
472 ParseTextAttribs (ColorClass cc, char *s)
474 (&appData.colorShout)[cc] = strdup(s);
478 ParseBoardSize (void *addr, char *name)
480 appData.boardSize = strdup(name);
485 { // In XBoard the sound-playing program takes care of obtaining the actual sound
489 SetCommPortDefaults ()
490 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
493 // [HGM] args: these three cases taken out to stay in front-end
495 SaveFontArg (FILE *f, ArgDescriptor *ad)
498 int i, n = (int)(intptr_t)ad->argLoc;
500 case 0: // CLOCK_FONT
501 name = appData.clockFont;
503 case 1: // MESSAGE_FONT
506 case 2: // COORD_FONT
507 name = appData.coordFont;
512 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
513 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
514 fontTable[n][squareSize] = strdup(name);
515 fontValid[n][squareSize] = True;
518 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
519 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
524 { // nothing to do, as the sounds are at all times represented by their text-string names already
528 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
529 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
530 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
534 SaveColor (FILE *f, ArgDescriptor *ad)
535 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
536 if(colorVariable[(int)(intptr_t)ad->argLoc])
537 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
541 SaveBoardSize (FILE *f, char *name, void *addr)
542 { // wrapper to shield back-end from BoardSize & sizeInfo
543 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
547 ParseCommPortSettings (char *s)
548 { // no such option in XBoard (yet)
554 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
558 gtk_widget_get_allocation(shell, &a);
562 wp->height = a.height;
563 printf("placement\n");
564 frameX = a.x; frameY = a.y; // remember to decide if windows touch
568 GetActualPlacement (Widget wg, WindowPlacement *wp)
570 XWindowAttributes winAt;
577 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
578 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
579 wp->x = rx - winAt.x;
580 wp->y = ry - winAt.y;
581 wp->height = winAt.height;
582 wp->width = winAt.width;
583 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
589 { // wrapper to shield use of window handles from back-end (make addressible by number?)
590 // In XBoard this will have to wait until awareness of window parameters is implemented
591 GetActualPlacement(shellWidget, &wpMain);
592 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
593 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
594 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
595 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
596 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
597 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
601 PrintCommPortSettings (FILE *f, char *name)
602 { // This option does not exist in XBoard
606 EnsureOnScreen (int *x, int *y, int minX, int minY)
613 { // [HGM] args: allows testing if main window is realized from back-end
614 return DialogExists(BoardWindow);
618 PopUpStartupDialog ()
619 { // start menu not implemented in XBoard
623 ConvertToLine (int argc, char **argv)
625 static char line[128*1024], buf[1024];
629 for(i=1; i<argc; i++)
631 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
632 && argv[i][0] != '{' )
633 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
635 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
636 strncat(line, buf, 128*1024 - strlen(line) - 1 );
639 line[strlen(line)-1] = NULLCHAR;
643 //--------------------------------------------------------------------------------------------
646 ResizeBoardWindow (int w, int h, int inhibit)
648 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
650 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
652 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
654 shellArgs[0].value = w;
655 shellArgs[1].value = h;
656 shellArgs[4].value = shellArgs[2].value = w;
657 shellArgs[5].value = shellArgs[3].value = h;
658 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
660 XSync(xDisplay, False);
666 { // dummy, as the GTK code does not make colors in advance
671 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
672 { // determine what fonts to use, and create them
677 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
678 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
679 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
680 appData.font = fontTable[MESSAGE_FONT][squareSize];
681 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
682 appData.coordFont = fontTable[COORD_FONT][squareSize];
685 appData.font = InsertPxlSize(appData.font, fontPxlSize);
686 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
687 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
688 fontSet = CreateFontSet(appData.font);
689 clockFontSet = CreateFontSet(appData.clockFont);
691 /* For the coordFont, use the 0th font of the fontset. */
692 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
693 XFontStruct **font_struct_list;
694 XFontSetExtents *fontSize;
695 char **font_name_list;
696 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
697 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
698 coordFontStruct = XQueryFont(xDisplay, coordFontID);
699 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
700 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
703 appData.font = FindFont(appData.font, fontPxlSize);
704 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
705 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
706 clockFontID = XLoadFont(xDisplay, appData.clockFont);
707 clockFontStruct = XQueryFont(xDisplay, clockFontID);
708 coordFontID = XLoadFont(xDisplay, appData.coordFont);
709 coordFontStruct = XQueryFont(xDisplay, coordFontID);
710 // textHeight in !NLS mode!
712 countFontID = coordFontID; // [HGM] holdings
713 countFontStruct = coordFontStruct;
715 xdb = XtDatabase(xDisplay);
717 XrmPutLineResource(&xdb, "*international: True");
718 vTo.size = sizeof(XFontSet);
719 vTo.addr = (XtPointer) &fontSet;
720 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
722 XrmPutStringResource(&xdb, "*font", appData.font);
733 case ArgInt: p = " N"; break;
734 case ArgString: p = " STR"; break;
735 case ArgBoolean: p = " TF"; break;
736 case ArgSettingsFilename:
737 case ArgFilename: p = " FILE"; break;
738 case ArgX: p = " Nx"; break;
739 case ArgY: p = " Ny"; break;
740 case ArgAttribs: p = " TEXTCOL"; break;
741 case ArgColor: p = " COL"; break;
742 case ArgFont: p = " FONT"; break;
743 case ArgBoardSize: p = " SIZE"; break;
744 case ArgFloat: p = " FLOAT"; break;
749 case ArgCommSettings:
760 ArgDescriptor *q, *p = argDescriptors+5;
761 printf("\nXBoard accepts the following options:\n"
762 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
763 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
764 " SIZE = board-size spec(s)\n"
765 " Within parentheses are short forms, or options to set to true or false.\n"
766 " Persistent options (saved in the settings file) are marked with *)\n\n");
768 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
769 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
770 if(p->save) strcat(buf+len, "*");
771 for(q=p+1; q->argLoc == p->argLoc; q++) {
772 if(q->argName[0] == '-') continue;
773 strcat(buf+len, q == p+1 ? " (" : " ");
774 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
776 if(q != p+1) strcat(buf+len, ")");
778 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
781 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
785 main (int argc, char **argv)
787 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
788 int boardWidth, boardHeight, w, h;
790 int forceMono = False;
792 srandom(time(0)); // [HGM] book: make random truly random
794 setbuf(stdout, NULL);
795 setbuf(stderr, NULL);
798 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
799 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
803 if(argc > 1 && !strcmp(argv[1], "--help" )) {
809 gtk_init (&argc, &argv);
811 programName = strrchr(argv[0], '/');
812 if (programName == NULL)
813 programName = argv[0];
818 // if (appData.debugMode) {
819 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
822 bindtextdomain(PACKAGE, LOCALEDIR);
826 appData.boardSize = "";
827 InitAppData(ConvertToLine(argc, argv));
829 if (p == NULL) p = "/tmp";
830 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
831 gameCopyFilename = (char*) malloc(i);
832 gamePasteFilename = (char*) malloc(i);
833 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
834 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
836 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
837 static char buf[MSG_SIZ];
838 EscapeExpand(buf, appData.firstInitString);
839 appData.firstInitString = strdup(buf);
840 EscapeExpand(buf, appData.secondInitString);
841 appData.secondInitString = strdup(buf);
842 EscapeExpand(buf, appData.firstComputerString);
843 appData.firstComputerString = strdup(buf);
844 EscapeExpand(buf, appData.secondComputerString);
845 appData.secondComputerString = strdup(buf);
848 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
851 if (chdir(chessDir) != 0) {
852 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
858 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
859 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
860 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
861 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
864 setbuf(debugFP, NULL);
868 if (appData.debugMode) {
869 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
873 /* [HGM,HR] make sure board size is acceptable */
874 if(appData.NrFiles > BOARD_FILES ||
875 appData.NrRanks > BOARD_RANKS )
876 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
879 /* This feature does not work; animation needs a rewrite */
880 appData.highlightDragging = FALSE;
884 gameInfo.variant = StringToVariant(appData.variant);
888 * determine size, based on supplied or remembered -size, or screen size
890 if (isdigit(appData.boardSize[0])) {
891 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
892 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
893 &fontPxlSize, &smallLayout, &tinyLayout);
895 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
896 programName, appData.boardSize);
900 /* Find some defaults; use the nearest known size */
901 SizeDefaults *szd, *nearest;
902 int distance = 99999;
903 nearest = szd = sizeDefaults;
904 while (szd->name != NULL) {
905 if (abs(szd->squareSize - squareSize) < distance) {
907 distance = abs(szd->squareSize - squareSize);
908 if (distance == 0) break;
912 if (i < 2) lineGap = nearest->lineGap;
913 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
914 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
915 if (i < 5) fontPxlSize = nearest->fontPxlSize;
916 if (i < 6) smallLayout = nearest->smallLayout;
917 if (i < 7) tinyLayout = nearest->tinyLayout;
920 SizeDefaults *szd = sizeDefaults;
921 if (*appData.boardSize == NULLCHAR) {
922 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
923 guint screenwidth = gdk_screen_get_width(screen);
924 guint screenheight = gdk_screen_get_height(screen);
925 while (screenwidth < szd->minScreenSize ||
926 screenheight < szd->minScreenSize) {
929 if (szd->name == NULL) szd--;
930 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
932 while (szd->name != NULL &&
933 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
934 if (szd->name == NULL) {
935 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
936 programName, appData.boardSize);
940 squareSize = szd->squareSize;
941 lineGap = szd->lineGap;
942 clockFontPxlSize = szd->clockFontPxlSize;
943 coordFontPxlSize = szd->coordFontPxlSize;
944 fontPxlSize = szd->fontPxlSize;
945 smallLayout = szd->smallLayout;
946 tinyLayout = szd->tinyLayout;
947 // [HGM] font: use defaults from settings file if available and not overruled
950 defaultLineGap = lineGap;
951 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
953 /* [HR] height treated separately (hacked) */
954 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
955 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
958 * Determine what fonts to use.
961 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
965 * Detect if there are not enough colors available and adapt.
968 if (DefaultDepth(xDisplay, xScreen) <= 2) {
969 appData.monoMode = True;
973 forceMono = MakeColors();
976 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
978 appData.monoMode = True;
981 ParseIcsTextColors();
987 layoutName = "tinyLayout";
988 } else if (smallLayout) {
989 layoutName = "smallLayout";
991 layoutName = "normalLayout";
994 optList = BoardPopUp(squareSize, lineGap, (void*)
1004 InitDrawingHandle(optList + W_BOARD);
1005 shellWidget = shells[BoardWindow];
1006 currBoard = &optList[W_BOARD];
1007 boardWidget = optList[W_BOARD].handle;
1008 menuBarWidget = optList[W_MENU].handle;
1009 dropMenu = optList[W_DROP].handle;
1010 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1012 formWidget = XtParent(boardWidget);
1013 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1014 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1015 XtGetValues(optList[W_WHITE].handle, args, 2);
1016 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1017 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1018 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1019 XtGetValues(optList[W_PAUSE].handle, args, 2);
1023 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1024 // not need to go into InitDrawingSizes().
1029 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1031 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
1032 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
1033 mainwindowIcon = WhiteIcon;
1034 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1038 * Create a cursor for the board widget.
1041 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1042 XChangeWindowAttributes(xDisplay, xBoardWindow,
1043 CWCursor, &window_attributes);
1047 * Inhibit shell resizing.
1050 shellArgs[0].value = (XtArgVal) &w;
1051 shellArgs[1].value = (XtArgVal) &h;
1052 XtGetValues(shellWidget, shellArgs, 2);
1053 shellArgs[4].value = shellArgs[2].value = w;
1054 shellArgs[5].value = shellArgs[3].value = h;
1055 // XtSetValues(shellWidget, &shellArgs[2], 4);
1059 gtk_widget_get_allocation(shells[BoardWindow], &a);
1060 w = a.width; h = a.height;
1061 //printf("start size (%d,%d), %dx%d\n", a.x, a.y, w, h);
1062 gtk_widget_get_allocation(boardWidget, &a);
1063 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1064 marginH = h - a.height - 25; // subtract 25, because GTK seems to insert this amount of extra empty space
1065 gtk_window_resize(GTK_WINDOW(shellWidget), marginW + boardWidth, marginH + boardHeight);
1066 //printf("margins h=%d v=%d\n", marginW, marginH);
1072 if(appData.logoSize)
1073 { // locate and read user logo
1075 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1076 ASSIGN(userLogo, buf);
1079 if (appData.animate || appData.animateDragging)
1082 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1083 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1085 /* [AS] Restore layout */
1086 if( wpMoveHistory.visible ) {
1090 if( wpEvalGraph.visible )
1095 if( wpEngineOutput.visible ) {
1096 EngineOutputPopUp();
1101 if (errorExitStatus == -1) {
1102 if (appData.icsActive) {
1103 /* We now wait until we see "login:" from the ICS before
1104 sending the logon script (problems with timestamp otherwise) */
1105 /*ICSInitScript();*/
1106 if (appData.icsInputBox) ICSInputBoxPopUp();
1110 signal(SIGWINCH, TermSizeSigHandler);
1112 signal(SIGINT, IntSigHandler);
1113 signal(SIGTERM, IntSigHandler);
1114 if (*appData.cmailGameName != NULLCHAR) {
1115 signal(SIGUSR1, CmailSigHandler);
1119 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1122 // XtSetKeyboardFocus(shellWidget, formWidget);
1124 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1127 /* check for GTK events and process them */
1130 gtk_main_iteration();
1133 if (appData.debugMode) fclose(debugFP); // [DM] debug
1138 TermSizeSigHandler (int sig)
1144 IntSigHandler (int sig)
1150 CmailSigHandler (int sig)
1155 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1157 /* Activate call-back function CmailSigHandlerCallBack() */
1158 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1160 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1164 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1167 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1169 /**** end signal code ****/
1172 #define Abs(n) ((n)<0 ? -(n) : (n))
1176 InsertPxlSize (char *pattern, int targetPxlSize)
1178 char *base_fnt_lst, strInt[12], *p, *q;
1179 int alternatives, i, len, strIntLen;
1182 * Replace the "*" (if present) in the pixel-size slot of each
1183 * alternative with the targetPxlSize.
1187 while ((p = strchr(p, ',')) != NULL) {
1191 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1192 strIntLen = strlen(strInt);
1193 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1197 while (alternatives--) {
1198 char *comma = strchr(p, ',');
1199 for (i=0; i<14; i++) {
1200 char *hyphen = strchr(p, '-');
1202 if (comma && hyphen > comma) break;
1203 len = hyphen + 1 - p;
1204 if (i == 7 && *p == '*' && len == 2) {
1206 memcpy(q, strInt, strIntLen);
1216 len = comma + 1 - p;
1223 return base_fnt_lst;
1228 CreateFontSet (char *base_fnt_lst)
1231 char **missing_list;
1235 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1236 &missing_list, &missing_count, &def_string);
1237 if (appData.debugMode) {
1239 XFontStruct **font_struct_list;
1240 char **font_name_list;
1241 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1243 fprintf(debugFP, " got list %s, locale %s\n",
1244 XBaseFontNameListOfFontSet(fntSet),
1245 XLocaleOfFontSet(fntSet));
1246 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1247 for (i = 0; i < count; i++) {
1248 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1251 for (i = 0; i < missing_count; i++) {
1252 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1255 if (fntSet == NULL) {
1256 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1262 #else // not ENABLE_NLS
1264 * Find a font that matches "pattern" that is as close as
1265 * possible to the targetPxlSize. Prefer fonts that are k
1266 * pixels smaller to fonts that are k pixels larger. The
1267 * pattern must be in the X Consortium standard format,
1268 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1269 * The return value should be freed with XtFree when no
1273 FindFont (char *pattern, int targetPxlSize)
1275 char **fonts, *p, *best, *scalable, *scalableTail;
1276 int i, j, nfonts, minerr, err, pxlSize;
1279 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1281 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1282 programName, pattern);
1289 for (i=0; i<nfonts; i++) {
1292 if (*p != '-') continue;
1294 if (*p == NULLCHAR) break;
1295 if (*p++ == '-') j++;
1297 if (j < 7) continue;
1300 scalable = fonts[i];
1303 err = pxlSize - targetPxlSize;
1304 if (Abs(err) < Abs(minerr) ||
1305 (minerr > 0 && err < 0 && -err == minerr)) {
1311 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1312 /* If the error is too big and there is a scalable font,
1313 use the scalable font. */
1314 int headlen = scalableTail - scalable;
1315 p = (char *) XtMalloc(strlen(scalable) + 10);
1316 while (isdigit(*scalableTail)) scalableTail++;
1317 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1319 p = (char *) XtMalloc(strlen(best) + 2);
1320 safeStrCpy(p, best, strlen(best)+1 );
1322 if (appData.debugMode) {
1323 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1324 pattern, targetPxlSize, p);
1326 XFreeFontNames(fonts);
1333 EnableNamedMenuItem (char *menuRef, int state)
1335 MenuItem *item = MenuNameToItem(menuRef);
1337 if(item) gtk_widget_set_sensitive(item->handle, state);
1341 EnableButtonBar (int state)
1344 XtSetSensitive(optList[W_BUTTON].handle, state);
1350 SetMenuEnables (Enables *enab)
1352 while (enab->name != NULL) {
1353 EnableNamedMenuItem(enab->name, enab->value);
1358 gboolean KeyPressProc(window, eventkey, data)
1360 GdkEventKey *eventkey;
1364 MoveTypeInProc(eventkey); // pop up for typed in moves
1367 /* check for other key values */
1368 switch(eventkey->keyval) {
1380 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1381 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1383 if(*nprms == 0) return;
1384 item = MenuNameToItem(prms[0]);
1385 if(item) ((MenuProc *) item->proc) ();
1399 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1400 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1401 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1402 dmEnables[i].piece);
1403 XtSetSensitive(entry, p != NULL || !appData.testLegality
1404 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1405 && !appData.icsActive));
1407 while (p && *p++ == dmEnables[i].piece) count++;
1408 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1410 XtSetArg(args[j], XtNlabel, label); j++;
1411 XtSetValues(entry, args, j);
1417 do_flash_delay (unsigned long msec)
1423 FlashDelay (int flash_delay)
1425 if(flash_delay) do_flash_delay(flash_delay);
1429 Fraction (int x, int start, int stop)
1431 double f = ((double) x - start)/(stop - start);
1432 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1436 static WindowPlacement wpNew;
1439 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1441 int touch=0, fudge = 2;
1442 GetActualPlacement(sh, wp);
1443 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1444 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1445 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1446 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1447 if(!touch ) return; // only windows that touch co-move
1448 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1449 int heightInc = wpNew.height - wpMain.height;
1450 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1451 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1452 wp->y += fracTop * heightInc;
1453 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1455 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1457 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1458 int widthInc = wpNew.width - wpMain.width;
1459 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1460 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1461 wp->y += fracLeft * widthInc;
1462 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1464 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1467 wp->x += wpNew.x - wpMain.x;
1468 wp->y += wpNew.y - wpMain.y;
1469 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1470 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1472 XtSetArg(args[j], XtNx, wp->x); j++;
1473 XtSetArg(args[j], XtNy, wp->y); j++;
1474 XtSetValues(sh, args, j);
1479 ReSize (WindowPlacement *wp)
1482 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1483 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1484 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1485 if(sqy < sqx) sqx = sqy;
1486 if(sqx != squareSize) {
1487 squareSize = sqx; // adopt new square size
1488 CreatePNGPieces(); // make newly scaled pieces
1489 InitDrawingSizes(0, 0); // creates grid etc.
1490 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1491 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1492 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1493 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1494 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1497 static guint delayedDragTag = 0;
1506 // GetActualPlacement(shellWidget, &wpNew);
1507 //printf("drag proc (%d,%d) %dx%d\n", wpNew.x, wpNew.y, wpNew.width, wpNew.height);
1508 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1509 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1510 busy = 0; return; // false alarm
1513 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1514 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1515 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1516 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1518 DrawPosition(True, NULL);
1519 if(delayedDragTag) g_source_remove(delayedDragTag);
1520 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1527 //printf("old timr = %d\n", delayedDragTag);
1528 if(delayedDragTag) g_source_remove(delayedDragTag);
1529 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1530 //printf("new timr = %d\n", delayedDragTag);
1534 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1536 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1538 wpNew.x = event->configure.x;
1539 wpNew.y = event->configure.y;
1540 wpNew.width = event->configure.width;
1541 wpNew.height = event->configure.height;
1542 if(appData.useStickyWindows)
1543 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1549 /* Disable all user input other than deleting the window */
1550 static int frozen = 0;
1556 /* Grab by a widget that doesn't accept input */
1557 gtk_grab_add(optList[W_MESSG].handle);
1561 /* Undo a FreezeUI */
1565 if (!frozen) return;
1566 gtk_grab_remove(optList[W_MESSG].handle);
1573 static int oldPausing = FALSE;
1574 static GameMode oldmode = (GameMode) -1;
1576 if (!boardWidget) return;
1578 if (pausing != oldPausing) {
1579 oldPausing = pausing;
1580 MarkMenuItem("Mode.Pause", pausing);
1582 if (appData.showButtonBar) {
1583 /* Always toggle, don't set. Previous code messes up when
1584 invoked while the button is pressed, as releasing it
1585 toggles the state again. */
1587 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1588 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1592 wname = ModeToWidgetName(oldmode);
1593 if (wname != NULL) {
1594 MarkMenuItem(wname, False);
1596 wname = ModeToWidgetName(gameMode);
1597 if (wname != NULL) {
1598 MarkMenuItem(wname, True);
1601 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1603 /* Maybe all the enables should be handled here, not just this one */
1604 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1606 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1611 * Button/menu procedures
1614 void CopyFileToClipboard(gchar *filename)
1616 gchar *selection_tmp;
1620 FILE* f = fopen(filename, "r");
1623 if (f == NULL) return;
1627 selection_tmp = g_try_malloc(len + 1);
1628 if (selection_tmp == NULL) {
1629 printf("Malloc failed in CopyFileToClipboard\n");
1632 count = fread(selection_tmp, 1, len, f);
1635 g_free(selection_tmp);
1638 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1640 // copy selection_tmp to clipboard
1641 GdkDisplay *gdisp = gdk_display_get_default();
1643 g_free(selection_tmp);
1646 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1647 gtk_clipboard_set_text(cb, selection_tmp, -1);
1648 g_free(selection_tmp);
1652 CopySomething (char *src)
1654 GdkDisplay *gdisp = gdk_display_get_default();
1656 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1657 if (gdisp == NULL) return;
1658 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1659 gtk_clipboard_set_text(cb, src, -1);
1663 PastePositionProc ()
1665 GdkDisplay *gdisp = gdk_display_get_default();
1669 if (gdisp == NULL) return;
1670 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1671 fenstr = gtk_clipboard_wait_for_text(cb);
1672 if (fenstr==NULL) return; // nothing had been selected to copy
1673 EditPositionPasteFEN(fenstr);
1685 // get game from clipboard
1686 GdkDisplay *gdisp = gdk_display_get_default();
1687 if (gdisp == NULL) return;
1688 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1689 text = gtk_clipboard_wait_for_text(cb);
1690 if (text == NULL) return; // nothing to paste
1693 // write to temp file
1694 if (text == NULL || len == 0) {
1695 return; //nothing to paste
1697 f = fopen(gamePasteFilename, "w");
1699 DisplayError(_("Can't open temp file"), errno);
1702 fwrite(text, 1, len, f);
1706 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1713 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1719 void MoveTypeInProc(eventkey)
1720 GdkEventKey *eventkey;
1724 // ingnore if ctrl or alt is pressed
1725 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
1729 buf[0]=eventkey->keyval;
1737 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1739 if (!TempBackwardActive) {
1740 TempBackwardActive = True;
1746 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1748 /* Check to see if triggered by a key release event for a repeating key.
1749 * If so the next queued event will be a key press of the same key at the same time */
1750 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1752 XPeekEvent(xDisplay, &next);
1753 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1754 next.xkey.keycode == event->xkey.keycode)
1758 TempBackwardActive = False;
1762 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1763 { // called as key binding
1766 if (nprms && *nprms > 0)
1770 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
1777 { // called from menu
1779 ManInner(NULL, NULL, NULL, NULL);
1784 SetWindowTitle (char *text, char *title, char *icon)
1789 if (appData.titleInWindow) {
1791 XtSetArg(args[i], XtNlabel, text); i++;
1792 XtSetValues(titleWidget, args, i);
1795 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1796 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1797 XtSetValues(shellWidget, args, i);
1798 XSync(xDisplay, False);
1800 if (appData.titleInWindow) {
1801 SetWidgetLabel(titleWidget, text);
1803 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1808 DisplayIcsInteractionTitle (String message)
1811 if (oldICSInteractionTitle == NULL) {
1812 /* Magic to find the old window title, adapted from vim */
1813 char *wina = getenv("WINDOWID");
1815 Window win = (Window) atoi(wina);
1816 Window root, parent, *children;
1817 unsigned int nchildren;
1818 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1820 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1821 if (!XQueryTree(xDisplay, win, &root, &parent,
1822 &children, &nchildren)) break;
1823 if (children) XFree((void *)children);
1824 if (parent == root || parent == 0) break;
1827 XSetErrorHandler(oldHandler);
1829 if (oldICSInteractionTitle == NULL) {
1830 oldICSInteractionTitle = "xterm";
1833 printf("\033]0;%s\007", message);
1840 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1842 GtkWidget *w = (GtkWidget *) opt->handle;
1848 strcpy(bgcolor, "black");
1849 strcpy(fgcolor, "white");
1851 strcpy(bgcolor, "white");
1852 strcpy(fgcolor, "black");
1855 appData.lowTimeWarning &&
1856 (timer / 1000) < appData.icsAlarmTime) {
1857 strcpy(fgcolor, appData.lowTimeWarningColor);
1860 if (appData.clockMode) {
1861 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1862 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1864 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
1865 bgcolor, fgcolor, color);
1867 gtk_label_set_markup(GTK_LABEL(w), markup);
1871 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1874 SetClockIcon (int color)
1876 GdkPixbuf *pm = *clockIcons[color];
1877 if (mainwindowIcon != pm) {
1878 mainwindowIcon = pm;
1879 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1883 #define INPUT_SOURCE_BUF_SIZE 8192
1892 char buf[INPUT_SOURCE_BUF_SIZE];
1897 DoInputCallback(io, cond, data)
1902 /* read input from one of the input source (for example a chess program, ICS, etc).
1903 * and call a function that will handle the input
1910 /* All information (callback function, file descriptor, etc) is
1911 * saved in an InputSource structure
1913 InputSource *is = (InputSource *) data;
1915 if (is->lineByLine) {
1916 count = read(is->fd, is->unused,
1917 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1919 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1922 is->unused += count;
1924 /* break input into lines and call the callback function on each
1927 while (p < is->unused) {
1928 q = memchr(p, '\n', is->unused - p);
1929 if (q == NULL) break;
1931 (is->func)(is, is->closure, p, q - p, 0);
1934 /* remember not yet used part of the buffer */
1936 while (p < is->unused) {
1941 /* read maximum length of input buffer and send the whole buffer
1942 * to the callback function
1944 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1949 (is->func)(is, is->closure, is->buf, count, error);
1951 return True; // Must return true or the watch will be removed
1954 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1961 GIOChannel *channel;
1962 ChildProc *cp = (ChildProc *) pr;
1964 is = (InputSource *) calloc(1, sizeof(InputSource));
1965 is->lineByLine = lineByLine;
1969 is->fd = fileno(stdin);
1971 is->kind = cp->kind;
1972 is->fd = cp->fdFrom;
1975 is->unused = is->buf;
1979 /* GTK-TODO: will this work on windows?*/
1981 channel = g_io_channel_unix_new(is->fd);
1982 g_io_channel_set_close_on_unref (channel, TRUE);
1983 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1985 is->closure = closure;
1986 return (InputSourceRef) is;
1991 RemoveInputSource(isr)
1994 InputSource *is = (InputSource *) isr;
1996 if (is->sid == 0) return;
1997 g_source_remove(is->sid);
2004 static Boolean frameWaiting;
2007 FrameAlarm (int sig)
2009 frameWaiting = False;
2010 /* In case System-V style signals. Needed?? */
2011 signal(SIGALRM, FrameAlarm);
2015 FrameDelay (int time)
2017 struct itimerval delay;
2020 frameWaiting = True;
2021 signal(SIGALRM, FrameAlarm);
2022 delay.it_interval.tv_sec =
2023 delay.it_value.tv_sec = time / 1000;
2024 delay.it_interval.tv_usec =
2025 delay.it_value.tv_usec = (time % 1000) * 1000;
2026 setitimer(ITIMER_REAL, &delay, NULL);
2027 while (frameWaiting) pause();
2028 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2029 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2030 setitimer(ITIMER_REAL, &delay, NULL);
2037 FrameDelay (int time)
2040 XSync(xDisplay, False);
2042 // gtk_main_iteration_do(False);
2045 usleep(time * 1000);
2051 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2053 char buf[MSG_SIZ], *logoName = buf;
2054 if(appData.logo[n][0]) {
2055 logoName = appData.logo[n];
2056 } else if(appData.autoLogo) {
2057 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2058 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2059 } else if(appData.directory[n] && appData.directory[n][0]) {
2060 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2064 { ASSIGN(cps->programLogo, logoName); }
2068 UpdateLogos (int displ)
2070 if(optList[W_WHITE-1].handle == NULL) return;
2071 LoadLogo(&first, 0, 0);
2072 LoadLogo(&second, 1, appData.icsActive);
2073 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2077 void FileNamePopUpGTK(label, def, filter, proc, pathFlag, openMode, name, fp)
2088 GtkFileFilter *gtkfilter;
2089 GtkFileFilter *gtkfilter_all;
2091 char fileext[10] = "";
2092 char *result = NULL;
2095 /* make a copy of the filter string, so that strtok can work with it*/
2096 cp = strndup(filter,strlen(filter));
2098 /* add filters for file extensions */
2099 gtkfilter = gtk_file_filter_new();
2100 gtkfilter_all = gtk_file_filter_new();
2102 /* one filter to show everything */
2103 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2104 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2106 /* add filter if present */
2107 result = strtok(cp, space);
2108 while( result != NULL ) {
2109 snprintf(fileext,10,"*%s",result);
2110 result = strtok( NULL, space );
2111 gtk_file_filter_add_pattern(gtkfilter, fileext);
2114 /* second filter to only show what's useful */
2115 gtk_file_filter_set_name (gtkfilter,filter);
2117 if (openMode[0] == 'r')
2119 dialog = gtk_file_chooser_dialog_new (label,
2121 GTK_FILE_CHOOSER_ACTION_OPEN,
2122 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2123 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2128 dialog = gtk_file_chooser_dialog_new (label,
2130 GTK_FILE_CHOOSER_ACTION_SAVE,
2131 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2132 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2134 /* add filename suggestions */
2135 if (strlen(def) > 0 )
2136 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2138 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2142 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2143 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2144 /* activate filter */
2145 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2147 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2152 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2155 f = fopen(filename, openMode);
2158 DisplayError(_("Failed to open file"), errno);
2162 /* TODO add indec */
2164 ASSIGN(*name, filename);
2165 ScheduleDelayedEvent(DelayedLoad, 50);
2170 gtk_widget_destroy (dialog);