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);
559 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
563 wp->height = a.height;
564 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
565 frameX = 3; frameY = 3; // remember to decide if windows touch
570 { // wrapper to shield use of window handles from back-end (make addressible by number?)
571 // In XBoard this will have to wait until awareness of window parameters is implemented
572 GetActualPlacement(shellWidget, &wpMain);
573 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
574 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
575 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
576 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
577 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
578 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
582 PrintCommPortSettings (FILE *f, char *name)
583 { // This option does not exist in XBoard
587 EnsureOnScreen (int *x, int *y, int minX, int minY)
594 { // [HGM] args: allows testing if main window is realized from back-end
595 return DialogExists(BoardWindow);
599 PopUpStartupDialog ()
600 { // start menu not implemented in XBoard
604 ConvertToLine (int argc, char **argv)
606 static char line[128*1024], buf[1024];
610 for(i=1; i<argc; i++)
612 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
613 && argv[i][0] != '{' )
614 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
616 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
617 strncat(line, buf, 128*1024 - strlen(line) - 1 );
620 line[strlen(line)-1] = NULLCHAR;
624 //--------------------------------------------------------------------------------------------
627 ResizeBoardWindow (int w, int h, int inhibit)
629 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
631 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
636 { // dummy, as the GTK code does not make colors in advance
641 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
642 { // determine what fonts to use, and create them
647 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
648 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
649 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
650 appData.font = fontTable[MESSAGE_FONT][squareSize];
651 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
652 appData.coordFont = fontTable[COORD_FONT][squareSize];
655 appData.font = InsertPxlSize(appData.font, fontPxlSize);
656 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
657 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
658 fontSet = CreateFontSet(appData.font);
659 clockFontSet = CreateFontSet(appData.clockFont);
661 /* For the coordFont, use the 0th font of the fontset. */
662 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
663 XFontStruct **font_struct_list;
664 XFontSetExtents *fontSize;
665 char **font_name_list;
666 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
667 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
668 coordFontStruct = XQueryFont(xDisplay, coordFontID);
669 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
670 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
673 appData.font = FindFont(appData.font, fontPxlSize);
674 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
675 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
676 clockFontID = XLoadFont(xDisplay, appData.clockFont);
677 clockFontStruct = XQueryFont(xDisplay, clockFontID);
678 coordFontID = XLoadFont(xDisplay, appData.coordFont);
679 coordFontStruct = XQueryFont(xDisplay, coordFontID);
680 // textHeight in !NLS mode!
682 countFontID = coordFontID; // [HGM] holdings
683 countFontStruct = coordFontStruct;
685 xdb = XtDatabase(xDisplay);
687 XrmPutLineResource(&xdb, "*international: True");
688 vTo.size = sizeof(XFontSet);
689 vTo.addr = (XtPointer) &fontSet;
690 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
692 XrmPutStringResource(&xdb, "*font", appData.font);
703 case ArgInt: p = " N"; break;
704 case ArgString: p = " STR"; break;
705 case ArgBoolean: p = " TF"; break;
706 case ArgSettingsFilename:
707 case ArgFilename: p = " FILE"; break;
708 case ArgX: p = " Nx"; break;
709 case ArgY: p = " Ny"; break;
710 case ArgAttribs: p = " TEXTCOL"; break;
711 case ArgColor: p = " COL"; break;
712 case ArgFont: p = " FONT"; break;
713 case ArgBoardSize: p = " SIZE"; break;
714 case ArgFloat: p = " FLOAT"; break;
719 case ArgCommSettings:
730 ArgDescriptor *q, *p = argDescriptors+5;
731 printf("\nXBoard accepts the following options:\n"
732 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
733 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
734 " SIZE = board-size spec(s)\n"
735 " Within parentheses are short forms, or options to set to true or false.\n"
736 " Persistent options (saved in the settings file) are marked with *)\n\n");
738 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
739 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
740 if(p->save) strcat(buf+len, "*");
741 for(q=p+1; q->argLoc == p->argLoc; q++) {
742 if(q->argName[0] == '-') continue;
743 strcat(buf+len, q == p+1 ? " (" : " ");
744 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
746 if(q != p+1) strcat(buf+len, ")");
748 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
751 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
755 main (int argc, char **argv)
757 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
758 int boardWidth, boardHeight, w, h;
760 int forceMono = False;
762 srandom(time(0)); // [HGM] book: make random truly random
764 setbuf(stdout, NULL);
765 setbuf(stderr, NULL);
768 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
769 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
773 if(argc > 1 && !strcmp(argv[1], "--help" )) {
779 gtk_init (&argc, &argv);
781 programName = strrchr(argv[0], '/');
782 if (programName == NULL)
783 programName = argv[0];
788 // if (appData.debugMode) {
789 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
792 bindtextdomain(PACKAGE, LOCALEDIR);
796 appData.boardSize = "";
797 InitAppData(ConvertToLine(argc, argv));
799 if (p == NULL) p = "/tmp";
800 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
801 gameCopyFilename = (char*) malloc(i);
802 gamePasteFilename = (char*) malloc(i);
803 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
804 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
806 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
807 static char buf[MSG_SIZ];
808 EscapeExpand(buf, appData.firstInitString);
809 appData.firstInitString = strdup(buf);
810 EscapeExpand(buf, appData.secondInitString);
811 appData.secondInitString = strdup(buf);
812 EscapeExpand(buf, appData.firstComputerString);
813 appData.firstComputerString = strdup(buf);
814 EscapeExpand(buf, appData.secondComputerString);
815 appData.secondComputerString = strdup(buf);
818 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
821 if (chdir(chessDir) != 0) {
822 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
828 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
829 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
830 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
831 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
834 setbuf(debugFP, NULL);
838 if (appData.debugMode) {
839 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
843 /* [HGM,HR] make sure board size is acceptable */
844 if(appData.NrFiles > BOARD_FILES ||
845 appData.NrRanks > BOARD_RANKS )
846 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
849 /* This feature does not work; animation needs a rewrite */
850 appData.highlightDragging = FALSE;
854 gameInfo.variant = StringToVariant(appData.variant);
858 * determine size, based on supplied or remembered -size, or screen size
860 if (isdigit(appData.boardSize[0])) {
861 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
862 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
863 &fontPxlSize, &smallLayout, &tinyLayout);
865 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
866 programName, appData.boardSize);
870 /* Find some defaults; use the nearest known size */
871 SizeDefaults *szd, *nearest;
872 int distance = 99999;
873 nearest = szd = sizeDefaults;
874 while (szd->name != NULL) {
875 if (abs(szd->squareSize - squareSize) < distance) {
877 distance = abs(szd->squareSize - squareSize);
878 if (distance == 0) break;
882 if (i < 2) lineGap = nearest->lineGap;
883 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
884 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
885 if (i < 5) fontPxlSize = nearest->fontPxlSize;
886 if (i < 6) smallLayout = nearest->smallLayout;
887 if (i < 7) tinyLayout = nearest->tinyLayout;
890 SizeDefaults *szd = sizeDefaults;
891 if (*appData.boardSize == NULLCHAR) {
892 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
893 guint screenwidth = gdk_screen_get_width(screen);
894 guint screenheight = gdk_screen_get_height(screen);
895 while (screenwidth < szd->minScreenSize ||
896 screenheight < szd->minScreenSize) {
899 if (szd->name == NULL) szd--;
900 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
902 while (szd->name != NULL &&
903 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
904 if (szd->name == NULL) {
905 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
906 programName, appData.boardSize);
910 squareSize = szd->squareSize;
911 lineGap = szd->lineGap;
912 clockFontPxlSize = szd->clockFontPxlSize;
913 coordFontPxlSize = szd->coordFontPxlSize;
914 fontPxlSize = szd->fontPxlSize;
915 smallLayout = szd->smallLayout;
916 tinyLayout = szd->tinyLayout;
917 // [HGM] font: use defaults from settings file if available and not overruled
920 defaultLineGap = lineGap;
921 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
923 /* [HR] height treated separately (hacked) */
924 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
925 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
928 * Determine what fonts to use.
931 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
935 * Detect if there are not enough colors available and adapt.
938 if (DefaultDepth(xDisplay, xScreen) <= 2) {
939 appData.monoMode = True;
943 forceMono = MakeColors();
946 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
948 appData.monoMode = True;
951 ParseIcsTextColors();
957 layoutName = "tinyLayout";
958 } else if (smallLayout) {
959 layoutName = "smallLayout";
961 layoutName = "normalLayout";
964 optList = BoardPopUp(squareSize, lineGap, (void*)
974 InitDrawingHandle(optList + W_BOARD);
975 shellWidget = shells[BoardWindow];
976 currBoard = &optList[W_BOARD];
977 boardWidget = optList[W_BOARD].handle;
978 menuBarWidget = optList[W_MENU].handle;
979 dropMenu = optList[W_DROP].handle;
980 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
982 formWidget = XtParent(boardWidget);
983 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
984 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
985 XtGetValues(optList[W_WHITE].handle, args, 2);
986 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
987 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
988 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
989 XtGetValues(optList[W_PAUSE].handle, args, 2);
993 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
994 // not need to go into InitDrawingSizes().
999 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1001 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
1002 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
1003 mainwindowIcon = WhiteIcon;
1004 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1008 * Create a cursor for the board widget.
1011 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1012 XChangeWindowAttributes(xDisplay, xBoardWindow,
1013 CWCursor, &window_attributes);
1017 * Inhibit shell resizing.
1020 shellArgs[0].value = (XtArgVal) &w;
1021 shellArgs[1].value = (XtArgVal) &h;
1022 XtGetValues(shellWidget, shellArgs, 2);
1023 shellArgs[4].value = shellArgs[2].value = w;
1024 shellArgs[5].value = shellArgs[3].value = h;
1025 // XtSetValues(shellWidget, &shellArgs[2], 4);
1029 gtk_widget_get_allocation(shells[BoardWindow], &a);
1030 w = a.width; h = a.height;
1031 //printf("start size (%d,%d), %dx%d\n", a.x, a.y, w, h);
1032 gtk_widget_get_allocation(boardWidget, &a);
1033 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1034 marginH = h - a.height - 25; // subtract 25, because GTK seems to insert this amount of extra empty space
1035 gtk_window_resize(GTK_WINDOW(shellWidget), marginW + boardWidth, marginH + boardHeight);
1036 //printf("margins h=%d v=%d\n", marginW, marginH);
1042 if(appData.logoSize)
1043 { // locate and read user logo
1045 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1046 ASSIGN(userLogo, buf);
1049 if (appData.animate || appData.animateDragging)
1052 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1053 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1055 /* [AS] Restore layout */
1056 if( wpMoveHistory.visible ) {
1060 if( wpEvalGraph.visible )
1065 if( wpEngineOutput.visible ) {
1066 EngineOutputPopUp();
1071 if (errorExitStatus == -1) {
1072 if (appData.icsActive) {
1073 /* We now wait until we see "login:" from the ICS before
1074 sending the logon script (problems with timestamp otherwise) */
1075 /*ICSInitScript();*/
1076 if (appData.icsInputBox) ICSInputBoxPopUp();
1080 signal(SIGWINCH, TermSizeSigHandler);
1082 signal(SIGINT, IntSigHandler);
1083 signal(SIGTERM, IntSigHandler);
1084 if (*appData.cmailGameName != NULLCHAR) {
1085 signal(SIGUSR1, CmailSigHandler);
1089 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1092 // XtSetKeyboardFocus(shellWidget, formWidget);
1094 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1097 /* check for GTK events and process them */
1100 gtk_main_iteration();
1103 if (appData.debugMode) fclose(debugFP); // [DM] debug
1108 TermSizeSigHandler (int sig)
1114 IntSigHandler (int sig)
1120 CmailSigHandler (int sig)
1125 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1127 /* Activate call-back function CmailSigHandlerCallBack() */
1128 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1130 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1134 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1137 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1139 /**** end signal code ****/
1142 #define Abs(n) ((n)<0 ? -(n) : (n))
1146 InsertPxlSize (char *pattern, int targetPxlSize)
1148 char *base_fnt_lst, strInt[12], *p, *q;
1149 int alternatives, i, len, strIntLen;
1152 * Replace the "*" (if present) in the pixel-size slot of each
1153 * alternative with the targetPxlSize.
1157 while ((p = strchr(p, ',')) != NULL) {
1161 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1162 strIntLen = strlen(strInt);
1163 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1167 while (alternatives--) {
1168 char *comma = strchr(p, ',');
1169 for (i=0; i<14; i++) {
1170 char *hyphen = strchr(p, '-');
1172 if (comma && hyphen > comma) break;
1173 len = hyphen + 1 - p;
1174 if (i == 7 && *p == '*' && len == 2) {
1176 memcpy(q, strInt, strIntLen);
1186 len = comma + 1 - p;
1193 return base_fnt_lst;
1198 CreateFontSet (char *base_fnt_lst)
1201 char **missing_list;
1205 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1206 &missing_list, &missing_count, &def_string);
1207 if (appData.debugMode) {
1209 XFontStruct **font_struct_list;
1210 char **font_name_list;
1211 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1213 fprintf(debugFP, " got list %s, locale %s\n",
1214 XBaseFontNameListOfFontSet(fntSet),
1215 XLocaleOfFontSet(fntSet));
1216 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1217 for (i = 0; i < count; i++) {
1218 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1221 for (i = 0; i < missing_count; i++) {
1222 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1225 if (fntSet == NULL) {
1226 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1232 #else // not ENABLE_NLS
1234 * Find a font that matches "pattern" that is as close as
1235 * possible to the targetPxlSize. Prefer fonts that are k
1236 * pixels smaller to fonts that are k pixels larger. The
1237 * pattern must be in the X Consortium standard format,
1238 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1239 * The return value should be freed with XtFree when no
1243 FindFont (char *pattern, int targetPxlSize)
1245 char **fonts, *p, *best, *scalable, *scalableTail;
1246 int i, j, nfonts, minerr, err, pxlSize;
1249 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1251 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1252 programName, pattern);
1259 for (i=0; i<nfonts; i++) {
1262 if (*p != '-') continue;
1264 if (*p == NULLCHAR) break;
1265 if (*p++ == '-') j++;
1267 if (j < 7) continue;
1270 scalable = fonts[i];
1273 err = pxlSize - targetPxlSize;
1274 if (Abs(err) < Abs(minerr) ||
1275 (minerr > 0 && err < 0 && -err == minerr)) {
1281 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1282 /* If the error is too big and there is a scalable font,
1283 use the scalable font. */
1284 int headlen = scalableTail - scalable;
1285 p = (char *) XtMalloc(strlen(scalable) + 10);
1286 while (isdigit(*scalableTail)) scalableTail++;
1287 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1289 p = (char *) XtMalloc(strlen(best) + 2);
1290 safeStrCpy(p, best, strlen(best)+1 );
1292 if (appData.debugMode) {
1293 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1294 pattern, targetPxlSize, p);
1296 XFreeFontNames(fonts);
1303 EnableNamedMenuItem (char *menuRef, int state)
1305 MenuItem *item = MenuNameToItem(menuRef);
1307 if(item) gtk_widget_set_sensitive(item->handle, state);
1311 EnableButtonBar (int state)
1314 XtSetSensitive(optList[W_BUTTON].handle, state);
1320 SetMenuEnables (Enables *enab)
1322 while (enab->name != NULL) {
1323 EnableNamedMenuItem(enab->name, enab->value);
1328 gboolean KeyPressProc(window, eventkey, data)
1330 GdkEventKey *eventkey;
1334 MoveTypeInProc(eventkey); // pop up for typed in moves
1337 /* check for other key values */
1338 switch(eventkey->keyval) {
1350 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1351 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1353 if(*nprms == 0) return;
1354 item = MenuNameToItem(prms[0]);
1355 if(item) ((MenuProc *) item->proc) ();
1369 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1370 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1371 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1372 dmEnables[i].piece);
1373 XtSetSensitive(entry, p != NULL || !appData.testLegality
1374 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1375 && !appData.icsActive));
1377 while (p && *p++ == dmEnables[i].piece) count++;
1378 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1380 XtSetArg(args[j], XtNlabel, label); j++;
1381 XtSetValues(entry, args, j);
1387 do_flash_delay (unsigned long msec)
1393 FlashDelay (int flash_delay)
1395 if(flash_delay) do_flash_delay(flash_delay);
1399 Fraction (int x, int start, int stop)
1401 double f = ((double) x - start)/(stop - start);
1402 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1406 static WindowPlacement wpNew;
1409 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1411 int touch=0, fudge = 2, f = 2;
1412 GetActualPlacement(sh, wp);
1413 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1414 if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x) < fudge) touch = 2; else // left touch
1415 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1416 if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y) < fudge) touch = 4; // top touch
1417 //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);
1418 if(!touch ) return; // only windows that touch co-move
1419 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1420 int heightInc = wpNew.height - wpMain.height;
1421 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1422 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1423 wp->y += fracTop * heightInc;
1424 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1426 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1428 wp->height += heightInc;
1429 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1430 int widthInc = wpNew.width - wpMain.width;
1431 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1432 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1433 wp->y += fracLeft * widthInc;
1434 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1436 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1438 wp->width += widthInc;
1440 wp->x += wpNew.x - wpMain.x;
1441 wp->y += wpNew.y - wpMain.y;
1442 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1443 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1445 XtSetArg(args[j], XtNx, wp->x); j++;
1446 XtSetArg(args[j], XtNy, wp->y); j++;
1447 XtSetValues(sh, args, j);
1449 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1450 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1451 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1455 ReSize (WindowPlacement *wp)
1458 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1459 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1460 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1461 if(sqy < sqx) sqx = sqy;
1462 if(sqx != squareSize) {
1463 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1464 squareSize = sqx; // adopt new square size
1465 CreatePNGPieces(); // make newly scaled pieces
1466 InitDrawingSizes(0, 0); // creates grid etc.
1467 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1468 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1469 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1470 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1471 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1474 static guint delayedDragTag = 0;
1483 // GetActualPlacement(shellWidget, &wpNew);
1484 //printf("drag proc (%d,%d) %dx%d\n", wpNew.x, wpNew.y, wpNew.width, wpNew.height);
1485 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1486 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1487 busy = 0; return; // false alarm
1490 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1491 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1492 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1493 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1495 DrawPosition(True, NULL);
1496 if(delayedDragTag) g_source_remove(delayedDragTag);
1497 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1504 //printf("old timr = %d\n", delayedDragTag);
1505 if(delayedDragTag) g_source_remove(delayedDragTag);
1506 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1507 //printf("new timr = %d\n", delayedDragTag);
1511 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1513 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1515 wpNew.x = event->configure.x;
1516 wpNew.y = event->configure.y;
1517 wpNew.width = event->configure.width;
1518 wpNew.height = event->configure.height;
1519 if(appData.useStickyWindows)
1520 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1526 /* Disable all user input other than deleting the window */
1527 static int frozen = 0;
1533 /* Grab by a widget that doesn't accept input */
1534 gtk_grab_add(optList[W_MESSG].handle);
1538 /* Undo a FreezeUI */
1542 if (!frozen) return;
1543 gtk_grab_remove(optList[W_MESSG].handle);
1550 static int oldPausing = FALSE;
1551 static GameMode oldmode = (GameMode) -1;
1553 if (!boardWidget) return;
1555 if (pausing != oldPausing) {
1556 oldPausing = pausing;
1557 MarkMenuItem("Mode.Pause", pausing);
1559 if (appData.showButtonBar) {
1560 /* Always toggle, don't set. Previous code messes up when
1561 invoked while the button is pressed, as releasing it
1562 toggles the state again. */
1564 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1565 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1569 wname = ModeToWidgetName(oldmode);
1570 if (wname != NULL) {
1571 MarkMenuItem(wname, False);
1573 wname = ModeToWidgetName(gameMode);
1574 if (wname != NULL) {
1575 MarkMenuItem(wname, True);
1578 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1580 /* Maybe all the enables should be handled here, not just this one */
1581 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1583 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1588 * Button/menu procedures
1591 void CopyFileToClipboard(gchar *filename)
1593 gchar *selection_tmp;
1597 FILE* f = fopen(filename, "r");
1600 if (f == NULL) return;
1604 selection_tmp = g_try_malloc(len + 1);
1605 if (selection_tmp == NULL) {
1606 printf("Malloc failed in CopyFileToClipboard\n");
1609 count = fread(selection_tmp, 1, len, f);
1612 g_free(selection_tmp);
1615 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1617 // copy selection_tmp to clipboard
1618 GdkDisplay *gdisp = gdk_display_get_default();
1620 g_free(selection_tmp);
1623 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1624 gtk_clipboard_set_text(cb, selection_tmp, -1);
1625 g_free(selection_tmp);
1629 CopySomething (char *src)
1631 GdkDisplay *gdisp = gdk_display_get_default();
1633 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1634 if (gdisp == NULL) return;
1635 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1636 gtk_clipboard_set_text(cb, src, -1);
1640 PastePositionProc ()
1642 GdkDisplay *gdisp = gdk_display_get_default();
1646 if (gdisp == NULL) return;
1647 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1648 fenstr = gtk_clipboard_wait_for_text(cb);
1649 if (fenstr==NULL) return; // nothing had been selected to copy
1650 EditPositionPasteFEN(fenstr);
1662 // get game from clipboard
1663 GdkDisplay *gdisp = gdk_display_get_default();
1664 if (gdisp == NULL) return;
1665 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1666 text = gtk_clipboard_wait_for_text(cb);
1667 if (text == NULL) return; // nothing to paste
1670 // write to temp file
1671 if (text == NULL || len == 0) {
1672 return; //nothing to paste
1674 f = fopen(gamePasteFilename, "w");
1676 DisplayError(_("Can't open temp file"), errno);
1679 fwrite(text, 1, len, f);
1683 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1690 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1696 void MoveTypeInProc(eventkey)
1697 GdkEventKey *eventkey;
1701 // ingnore if ctrl or alt is pressed
1702 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
1706 buf[0]=eventkey->keyval;
1714 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1716 if (!TempBackwardActive) {
1717 TempBackwardActive = True;
1723 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1725 /* Check to see if triggered by a key release event for a repeating key.
1726 * If so the next queued event will be a key press of the same key at the same time */
1727 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1729 XPeekEvent(xDisplay, &next);
1730 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1731 next.xkey.keycode == event->xkey.keycode)
1735 TempBackwardActive = False;
1739 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1740 { // called as key binding
1743 if (nprms && *nprms > 0)
1747 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
1754 { // called from menu
1756 ManInner(NULL, NULL, NULL, NULL);
1761 SetWindowTitle (char *text, char *title, char *icon)
1766 if (appData.titleInWindow) {
1768 XtSetArg(args[i], XtNlabel, text); i++;
1769 XtSetValues(titleWidget, args, i);
1772 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1773 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1774 XtSetValues(shellWidget, args, i);
1775 XSync(xDisplay, False);
1777 if (appData.titleInWindow) {
1778 SetWidgetLabel(titleWidget, text);
1780 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1785 DisplayIcsInteractionTitle (String message)
1788 if (oldICSInteractionTitle == NULL) {
1789 /* Magic to find the old window title, adapted from vim */
1790 char *wina = getenv("WINDOWID");
1792 Window win = (Window) atoi(wina);
1793 Window root, parent, *children;
1794 unsigned int nchildren;
1795 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1797 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1798 if (!XQueryTree(xDisplay, win, &root, &parent,
1799 &children, &nchildren)) break;
1800 if (children) XFree((void *)children);
1801 if (parent == root || parent == 0) break;
1804 XSetErrorHandler(oldHandler);
1806 if (oldICSInteractionTitle == NULL) {
1807 oldICSInteractionTitle = "xterm";
1810 printf("\033]0;%s\007", message);
1817 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1819 GtkWidget *w = (GtkWidget *) opt->handle;
1825 strcpy(bgcolor, "black");
1826 strcpy(fgcolor, "white");
1828 strcpy(bgcolor, "white");
1829 strcpy(fgcolor, "black");
1832 appData.lowTimeWarning &&
1833 (timer / 1000) < appData.icsAlarmTime) {
1834 strcpy(fgcolor, appData.lowTimeWarningColor);
1837 if (appData.clockMode) {
1838 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1839 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1841 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
1842 bgcolor, fgcolor, color);
1844 gtk_label_set_markup(GTK_LABEL(w), markup);
1848 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1851 SetClockIcon (int color)
1853 GdkPixbuf *pm = *clockIcons[color];
1854 if (mainwindowIcon != pm) {
1855 mainwindowIcon = pm;
1856 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1860 #define INPUT_SOURCE_BUF_SIZE 8192
1869 char buf[INPUT_SOURCE_BUF_SIZE];
1874 DoInputCallback(io, cond, data)
1879 /* read input from one of the input source (for example a chess program, ICS, etc).
1880 * and call a function that will handle the input
1887 /* All information (callback function, file descriptor, etc) is
1888 * saved in an InputSource structure
1890 InputSource *is = (InputSource *) data;
1892 if (is->lineByLine) {
1893 count = read(is->fd, is->unused,
1894 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1896 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1899 is->unused += count;
1901 /* break input into lines and call the callback function on each
1904 while (p < is->unused) {
1905 q = memchr(p, '\n', is->unused - p);
1906 if (q == NULL) break;
1908 (is->func)(is, is->closure, p, q - p, 0);
1911 /* remember not yet used part of the buffer */
1913 while (p < is->unused) {
1918 /* read maximum length of input buffer and send the whole buffer
1919 * to the callback function
1921 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1926 (is->func)(is, is->closure, is->buf, count, error);
1928 return True; // Must return true or the watch will be removed
1931 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1938 GIOChannel *channel;
1939 ChildProc *cp = (ChildProc *) pr;
1941 is = (InputSource *) calloc(1, sizeof(InputSource));
1942 is->lineByLine = lineByLine;
1946 is->fd = fileno(stdin);
1948 is->kind = cp->kind;
1949 is->fd = cp->fdFrom;
1952 is->unused = is->buf;
1956 /* GTK-TODO: will this work on windows?*/
1958 channel = g_io_channel_unix_new(is->fd);
1959 g_io_channel_set_close_on_unref (channel, TRUE);
1960 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1962 is->closure = closure;
1963 return (InputSourceRef) is;
1968 RemoveInputSource(isr)
1971 InputSource *is = (InputSource *) isr;
1973 if (is->sid == 0) return;
1974 g_source_remove(is->sid);
1981 static Boolean frameWaiting;
1984 FrameAlarm (int sig)
1986 frameWaiting = False;
1987 /* In case System-V style signals. Needed?? */
1988 signal(SIGALRM, FrameAlarm);
1992 FrameDelay (int time)
1994 struct itimerval delay;
1997 frameWaiting = True;
1998 signal(SIGALRM, FrameAlarm);
1999 delay.it_interval.tv_sec =
2000 delay.it_value.tv_sec = time / 1000;
2001 delay.it_interval.tv_usec =
2002 delay.it_value.tv_usec = (time % 1000) * 1000;
2003 setitimer(ITIMER_REAL, &delay, NULL);
2004 while (frameWaiting) pause();
2005 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2006 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2007 setitimer(ITIMER_REAL, &delay, NULL);
2014 FrameDelay (int time)
2017 XSync(xDisplay, False);
2019 // gtk_main_iteration_do(False);
2022 usleep(time * 1000);
2028 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2030 char buf[MSG_SIZ], *logoName = buf;
2031 if(appData.logo[n][0]) {
2032 logoName = appData.logo[n];
2033 } else if(appData.autoLogo) {
2034 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2035 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2036 } else if(appData.directory[n] && appData.directory[n][0]) {
2037 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2041 { ASSIGN(cps->programLogo, logoName); }
2045 UpdateLogos (int displ)
2047 if(optList[W_WHITE-1].handle == NULL) return;
2048 LoadLogo(&first, 0, 0);
2049 LoadLogo(&second, 1, appData.icsActive);
2050 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2054 void FileNamePopUpGTK(label, def, filter, proc, pathFlag, openMode, name, fp)
2065 GtkFileFilter *gtkfilter;
2066 GtkFileFilter *gtkfilter_all;
2068 char fileext[10] = "";
2069 char *result = NULL;
2072 /* make a copy of the filter string, so that strtok can work with it*/
2073 cp = strndup(filter,strlen(filter));
2075 /* add filters for file extensions */
2076 gtkfilter = gtk_file_filter_new();
2077 gtkfilter_all = gtk_file_filter_new();
2079 /* one filter to show everything */
2080 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2081 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2083 /* add filter if present */
2084 result = strtok(cp, space);
2085 while( result != NULL ) {
2086 snprintf(fileext,10,"*%s",result);
2087 result = strtok( NULL, space );
2088 gtk_file_filter_add_pattern(gtkfilter, fileext);
2091 /* second filter to only show what's useful */
2092 gtk_file_filter_set_name (gtkfilter,filter);
2094 if (openMode[0] == 'r')
2096 dialog = gtk_file_chooser_dialog_new (label,
2098 GTK_FILE_CHOOSER_ACTION_OPEN,
2099 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2100 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2105 dialog = gtk_file_chooser_dialog_new (label,
2107 GTK_FILE_CHOOSER_ACTION_SAVE,
2108 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2109 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2111 /* add filename suggestions */
2112 if (strlen(def) > 0 )
2113 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2115 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2119 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2120 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2121 /* activate filter */
2122 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2124 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2129 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2132 f = fopen(filename, openMode);
2135 DisplayError(_("Failed to open file"), errno);
2139 /* TODO add indec */
2141 ASSIGN(*name, filename);
2142 ScheduleDelayedEvent(DelayedLoad, 50);
2147 gtk_widget_destroy (dialog);