2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
64 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.h>
69 # if HAVE_SYS_SOCKET_H
70 # include <sys/socket.h>
71 # include <netinet/in.h>
73 # else /* not HAVE_SYS_SOCKET_H */
74 # if HAVE_LAN_SOCKET_H
75 # include <lan/socket.h>
77 # include <lan/netdb.h>
78 # else /* not HAVE_LAN_SOCKET_H */
79 # define OMIT_SOCKETS 1
80 # endif /* not HAVE_LAN_SOCKET_H */
81 # endif /* not HAVE_SYS_SOCKET_H */
82 #endif /* !OMIT_SOCKETS */
87 #else /* not STDC_HEADERS */
88 extern char *getenv();
91 # else /* not HAVE_STRING_H */
93 # endif /* not HAVE_STRING_H */
94 #endif /* not STDC_HEADERS */
97 # include <sys/fcntl.h>
98 #else /* not HAVE_SYS_FCNTL_H */
101 # endif /* HAVE_FCNTL_H */
102 #endif /* not HAVE_SYS_FCNTL_H */
104 #if HAVE_SYS_SYSTEMINFO_H
105 # include <sys/systeminfo.h>
106 #endif /* HAVE_SYS_SYSTEMINFO_H */
108 #if TIME_WITH_SYS_TIME
109 # include <sys/time.h>
113 # include <sys/time.h>
124 # include <sys/wait.h>
129 # define NAMLEN(dirent) strlen((dirent)->d_name)
130 # define HAVE_DIR_STRUCT
132 # define dirent direct
133 # define NAMLEN(dirent) (dirent)->d_namlen
135 # include <sys/ndir.h>
136 # define HAVE_DIR_STRUCT
139 # include <sys/dir.h>
140 # define HAVE_DIR_STRUCT
144 # define HAVE_DIR_STRUCT
152 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
155 #include "frontend.h"
157 #include "backendz.h"
165 #include "engineoutput.h"
175 #define usleep(t) _sleep2(((t)+500)/1000)
179 # define _(s) gettext (s)
180 # define N_(s) gettext_noop (s)
186 int main P((int argc, char **argv));
187 RETSIGTYPE CmailSigHandler P((int sig));
188 RETSIGTYPE IntSigHandler P((int sig));
189 RETSIGTYPE TermSizeSigHandler P((int sig));
191 char *InsertPxlSize P((char *pattern, int targetPxlSize));
192 XFontSet CreateFontSet P((char *base_fnt_lst));
194 char *FindFont P((char *pattern, int targetPxlSize));
196 void DelayedDrag P((void));
197 void ICSInputBoxPopUp P((void));
198 void MoveTypeInProc P((GdkEventKey *eventkey));
199 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
200 Boolean TempBackwardActive = False;
201 void DisplayMove P((int moveNumber));
202 void update_ics_width P(());
203 int CopyMemoProc P(());
204 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
208 XFontSet fontSet, clockFontSet;
211 XFontStruct *clockFontStruct;
213 Font coordFontID, countFontID;
214 XFontStruct *coordFontStruct, *countFontStruct;
216 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
217 GtkWidget *mainwindow;
219 Option *optList; // contains all widgets of main window
222 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
225 static GdkPixbuf *mainwindowIcon=NULL;
226 static GdkPixbuf *WhiteIcon=NULL;
227 static GdkPixbuf *BlackIcon=NULL;
229 typedef unsigned int BoardSize;
231 Boolean chessProgram;
233 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
234 int smallLayout = 0, tinyLayout = 0,
235 marginW, marginH, // [HGM] for run-time resizing
236 fromX = -1, fromY = -1, toX, toY, commentUp = False,
237 errorExitStatus = -1, defaultLineGap;
239 Dimension textHeight;
241 char *chessDir, *programName, *programVersion;
242 Boolean alwaysOnTop = False;
243 char *icsTextMenuString;
245 char *firstChessProgramNames;
246 char *secondChessProgramNames;
248 WindowPlacement wpMain;
249 WindowPlacement wpConsole;
250 WindowPlacement wpComment;
251 WindowPlacement wpMoveHistory;
252 WindowPlacement wpEvalGraph;
253 WindowPlacement wpEngineOutput;
254 WindowPlacement wpGameList;
255 WindowPlacement wpTags;
257 /* This magic number is the number of intermediate frames used
258 in each half of the animation. For short moves it's reduced
259 by 1. The total number of frames will be factor * 2 + 1. */
262 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
269 DropMenuEnables dmEnables[] = {
278 XtResource clientResources[] = {
279 { "flashCount", "flashCount", XtRInt, sizeof(int),
280 XtOffset(AppDataPtr, flashCount), XtRImmediate,
281 (XtPointer) FLASH_COUNT },
285 char globalTranslations[] =
286 ":<Key>F9: MenuItem(Actions.Resign) \n \
287 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
288 :Meta<Key>V: MenuItem(File.NewVariant) \n \
289 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
290 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
291 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
292 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
293 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
294 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
295 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
296 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
297 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
298 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
299 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
300 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
301 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
302 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
303 :Ctrl<Key>q: MenuItem(File.Quit) \n \
304 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
305 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
306 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
307 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
308 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
309 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
310 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
311 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
312 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
313 :Meta<Key>G: MenuItem(View.GameList) \n \
314 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
315 :<Key>Pause: MenuItem(Mode.Pause) \n \
316 :<Key>F3: MenuItem(Action.Accept) \n \
317 :<Key>F4: MenuItem(Action.Decline) \n \
318 :<Key>F12: MenuItem(Action.Rematch) \n \
319 :<Key>F5: MenuItem(Action.CallFlag) \n \
320 :<Key>F6: MenuItem(Action.Draw) \n \
321 :<Key>F7: MenuItem(Action.Adjourn) \n \
322 :<Key>F8: MenuItem(Action.Abort) \n \
323 :<Key>F10: MenuItem(Action.StopObserving) \n \
324 :<Key>F11: MenuItem(Action.StopExamining) \n \
325 :Ctrl<Key>d: MenuItem(DebugProc) \n \
326 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
327 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
328 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
329 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
330 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
331 :<Key>Left: MenuItem(Edit.Backward) \n \
332 :<Key>Right: MenuItem(Edit.Forward) \n \
333 :<Key>Home: MenuItem(Edit.Revert) \n \
334 :<Key>End: MenuItem(Edit.TruncateGame) \n \
335 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
336 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
337 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
338 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
339 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
340 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
341 #ifndef OPTIONSDIALOG
343 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
344 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
345 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
346 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
347 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
350 :<Key>F1: MenuItem(Help.ManXBoard) \n \
351 :<Key>F2: MenuItem(View.FlipView) \n \
352 :<KeyDown>Return: TempBackwardProc() \n \
353 :<KeyUp>Return: TempForwardProc() \n";
355 char ICSInputTranslations[] =
356 "<Key>Up: UpKeyProc() \n "
357 "<Key>Down: DownKeyProc() \n "
358 "<Key>Return: EnterKeyProc() \n";
360 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
361 // as the widget is destroyed before the up-click can call extend-end
362 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
365 String xboardResources[] = {
366 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
374 gtk_window_present(GTK_WINDOW(mainwindow));
377 //---------------------------------------------------------------------------------------------------------
378 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
381 #define CW_USEDEFAULT (1<<31)
382 #define ICS_TEXT_MENU_SIZE 90
383 #define DEBUG_FILE "xboard.debug"
384 #define SetCurrentDirectory chdir
385 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
389 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
392 // front-end part of option handling
394 // [HGM] This platform-dependent table provides the location for storing the color info
395 extern char *crWhite, * crBlack;
399 &appData.whitePieceColor,
400 &appData.blackPieceColor,
401 &appData.lightSquareColor,
402 &appData.darkSquareColor,
403 &appData.highlightSquareColor,
404 &appData.premoveHighlightColor,
405 &appData.lowTimeWarningColor,
416 // [HGM] font: keep a font for each square size, even non-stndard ones
419 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
420 char *fontTable[NUM_FONTS][MAX_SIZE];
423 ParseFont (char *name, int number)
424 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
426 if(sscanf(name, "size%d:", &size)) {
427 // [HGM] font: font is meant for specific boardSize (likely from settings file);
428 // defer processing it until we know if it matches our board size
429 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
430 fontTable[number][size] = strdup(strchr(name, ':')+1);
431 fontValid[number][size] = True;
436 case 0: // CLOCK_FONT
437 appData.clockFont = strdup(name);
439 case 1: // MESSAGE_FONT
440 appData.font = strdup(name);
442 case 2: // COORD_FONT
443 appData.coordFont = strdup(name);
448 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
453 { // only 2 fonts currently
454 appData.clockFont = CLOCK_FONT_NAME;
455 appData.coordFont = COORD_FONT_NAME;
456 appData.font = DEFAULT_FONT_NAME;
461 { // no-op, until we identify the code for this already in XBoard and move it here
465 ParseColor (int n, char *name)
466 { // in XBoard, just copy the color-name string
467 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
471 ParseTextAttribs (ColorClass cc, char *s)
473 (&appData.colorShout)[cc] = strdup(s);
477 ParseBoardSize (void *addr, char *name)
479 appData.boardSize = strdup(name);
484 { // In XBoard the sound-playing program takes care of obtaining the actual sound
488 SetCommPortDefaults ()
489 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
492 // [HGM] args: these three cases taken out to stay in front-end
494 SaveFontArg (FILE *f, ArgDescriptor *ad)
497 int i, n = (int)(intptr_t)ad->argLoc;
499 case 0: // CLOCK_FONT
500 name = appData.clockFont;
502 case 1: // MESSAGE_FONT
505 case 2: // COORD_FONT
506 name = appData.coordFont;
511 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
512 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
513 fontTable[n][squareSize] = strdup(name);
514 fontValid[n][squareSize] = True;
517 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
518 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
523 { // nothing to do, as the sounds are at all times represented by their text-string names already
527 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
528 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
529 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
533 SaveColor (FILE *f, ArgDescriptor *ad)
534 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
535 if(colorVariable[(int)(intptr_t)ad->argLoc])
536 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
540 SaveBoardSize (FILE *f, char *name, void *addr)
541 { // wrapper to shield back-end from BoardSize & sizeInfo
542 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
546 ParseCommPortSettings (char *s)
547 { // no such option in XBoard (yet)
553 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
557 gtk_widget_get_allocation(shell, &a);
558 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
562 wp->height = a.height;
563 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
564 frameX = 3; frameY = 3; // remember to decide if windows touch
569 { // wrapper to shield use of window handles from back-end (make addressible by number?)
570 // In XBoard this will have to wait until awareness of window parameters is implemented
571 GetActualPlacement(shellWidget, &wpMain);
572 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
573 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
574 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
575 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
576 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
577 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
581 PrintCommPortSettings (FILE *f, char *name)
582 { // This option does not exist in XBoard
586 EnsureOnScreen (int *x, int *y, int minX, int minY)
593 { // [HGM] args: allows testing if main window is realized from back-end
594 return DialogExists(BoardWindow);
598 PopUpStartupDialog ()
599 { // start menu not implemented in XBoard
603 ConvertToLine (int argc, char **argv)
605 static char line[128*1024], buf[1024];
609 for(i=1; i<argc; i++)
611 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
612 && argv[i][0] != '{' )
613 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
615 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
616 strncat(line, buf, 128*1024 - strlen(line) - 1 );
619 line[strlen(line)-1] = NULLCHAR;
623 //--------------------------------------------------------------------------------------------
626 ResizeBoardWindow (int w, int h, int inhibit)
628 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
630 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
635 { // dummy, as the GTK code does not make colors in advance
640 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
641 { // determine what fonts to use, and create them
646 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
647 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
648 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
649 appData.font = fontTable[MESSAGE_FONT][squareSize];
650 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
651 appData.coordFont = fontTable[COORD_FONT][squareSize];
654 appData.font = InsertPxlSize(appData.font, fontPxlSize);
655 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
656 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
657 fontSet = CreateFontSet(appData.font);
658 clockFontSet = CreateFontSet(appData.clockFont);
660 /* For the coordFont, use the 0th font of the fontset. */
661 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
662 XFontStruct **font_struct_list;
663 XFontSetExtents *fontSize;
664 char **font_name_list;
665 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
666 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
667 coordFontStruct = XQueryFont(xDisplay, coordFontID);
668 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
669 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
672 appData.font = FindFont(appData.font, fontPxlSize);
673 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
674 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
675 clockFontID = XLoadFont(xDisplay, appData.clockFont);
676 clockFontStruct = XQueryFont(xDisplay, clockFontID);
677 coordFontID = XLoadFont(xDisplay, appData.coordFont);
678 coordFontStruct = XQueryFont(xDisplay, coordFontID);
679 // textHeight in !NLS mode!
681 countFontID = coordFontID; // [HGM] holdings
682 countFontStruct = coordFontStruct;
684 xdb = XtDatabase(xDisplay);
686 XrmPutLineResource(&xdb, "*international: True");
687 vTo.size = sizeof(XFontSet);
688 vTo.addr = (XtPointer) &fontSet;
689 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
691 XrmPutStringResource(&xdb, "*font", appData.font);
702 case ArgInt: p = " N"; break;
703 case ArgString: p = " STR"; break;
704 case ArgBoolean: p = " TF"; break;
705 case ArgSettingsFilename:
706 case ArgFilename: p = " FILE"; break;
707 case ArgX: p = " Nx"; break;
708 case ArgY: p = " Ny"; break;
709 case ArgAttribs: p = " TEXTCOL"; break;
710 case ArgColor: p = " COL"; break;
711 case ArgFont: p = " FONT"; break;
712 case ArgBoardSize: p = " SIZE"; break;
713 case ArgFloat: p = " FLOAT"; break;
718 case ArgCommSettings:
729 ArgDescriptor *q, *p = argDescriptors+5;
730 printf("\nXBoard accepts the following options:\n"
731 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
732 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
733 " SIZE = board-size spec(s)\n"
734 " Within parentheses are short forms, or options to set to true or false.\n"
735 " Persistent options (saved in the settings file) are marked with *)\n\n");
737 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
738 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
739 if(p->save) strcat(buf+len, "*");
740 for(q=p+1; q->argLoc == p->argLoc; q++) {
741 if(q->argName[0] == '-') continue;
742 strcat(buf+len, q == p+1 ? " (" : " ");
743 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
745 if(q != p+1) strcat(buf+len, ")");
747 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
750 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
754 main (int argc, char **argv)
756 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
757 int boardWidth, boardHeight, w, h;
759 int forceMono = False;
761 srandom(time(0)); // [HGM] book: make random truly random
763 setbuf(stdout, NULL);
764 setbuf(stderr, NULL);
767 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
768 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
772 if(argc > 1 && !strcmp(argv[1], "--help" )) {
778 gtk_init (&argc, &argv);
780 programName = strrchr(argv[0], '/');
781 if (programName == NULL)
782 programName = argv[0];
787 // if (appData.debugMode) {
788 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
791 bindtextdomain(PACKAGE, LOCALEDIR);
795 appData.boardSize = "";
796 InitAppData(ConvertToLine(argc, argv));
798 if (p == NULL) p = "/tmp";
799 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
800 gameCopyFilename = (char*) malloc(i);
801 gamePasteFilename = (char*) malloc(i);
802 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
803 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
805 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
806 static char buf[MSG_SIZ];
807 EscapeExpand(buf, appData.firstInitString);
808 appData.firstInitString = strdup(buf);
809 EscapeExpand(buf, appData.secondInitString);
810 appData.secondInitString = strdup(buf);
811 EscapeExpand(buf, appData.firstComputerString);
812 appData.firstComputerString = strdup(buf);
813 EscapeExpand(buf, appData.secondComputerString);
814 appData.secondComputerString = strdup(buf);
817 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
820 if (chdir(chessDir) != 0) {
821 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
827 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
828 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
829 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
830 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
833 setbuf(debugFP, NULL);
837 if (appData.debugMode) {
838 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
842 /* [HGM,HR] make sure board size is acceptable */
843 if(appData.NrFiles > BOARD_FILES ||
844 appData.NrRanks > BOARD_RANKS )
845 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
848 /* This feature does not work; animation needs a rewrite */
849 appData.highlightDragging = FALSE;
853 gameInfo.variant = StringToVariant(appData.variant);
857 * determine size, based on supplied or remembered -size, or screen size
859 if (isdigit(appData.boardSize[0])) {
860 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
861 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
862 &fontPxlSize, &smallLayout, &tinyLayout);
864 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
865 programName, appData.boardSize);
869 /* Find some defaults; use the nearest known size */
870 SizeDefaults *szd, *nearest;
871 int distance = 99999;
872 nearest = szd = sizeDefaults;
873 while (szd->name != NULL) {
874 if (abs(szd->squareSize - squareSize) < distance) {
876 distance = abs(szd->squareSize - squareSize);
877 if (distance == 0) break;
881 if (i < 2) lineGap = nearest->lineGap;
882 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
883 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
884 if (i < 5) fontPxlSize = nearest->fontPxlSize;
885 if (i < 6) smallLayout = nearest->smallLayout;
886 if (i < 7) tinyLayout = nearest->tinyLayout;
889 SizeDefaults *szd = sizeDefaults;
890 if (*appData.boardSize == NULLCHAR) {
891 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
892 guint screenwidth = gdk_screen_get_width(screen);
893 guint screenheight = gdk_screen_get_height(screen);
894 while (screenwidth < szd->minScreenSize ||
895 screenheight < szd->minScreenSize) {
898 if (szd->name == NULL) szd--;
899 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
901 while (szd->name != NULL &&
902 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
903 if (szd->name == NULL) {
904 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
905 programName, appData.boardSize);
909 squareSize = szd->squareSize;
910 lineGap = szd->lineGap;
911 clockFontPxlSize = szd->clockFontPxlSize;
912 coordFontPxlSize = szd->coordFontPxlSize;
913 fontPxlSize = szd->fontPxlSize;
914 smallLayout = szd->smallLayout;
915 tinyLayout = szd->tinyLayout;
916 // [HGM] font: use defaults from settings file if available and not overruled
919 defaultLineGap = lineGap;
920 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
922 /* [HR] height treated separately (hacked) */
923 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
924 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
927 * Determine what fonts to use.
930 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
934 * Detect if there are not enough colors available and adapt.
937 if (DefaultDepth(xDisplay, xScreen) <= 2) {
938 appData.monoMode = True;
942 forceMono = MakeColors();
945 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
947 appData.monoMode = True;
950 ParseIcsTextColors();
956 layoutName = "tinyLayout";
957 } else if (smallLayout) {
958 layoutName = "smallLayout";
960 layoutName = "normalLayout";
963 optList = BoardPopUp(squareSize, lineGap, (void*)
973 InitDrawingHandle(optList + W_BOARD);
974 shellWidget = shells[BoardWindow];
975 currBoard = &optList[W_BOARD];
976 boardWidget = optList[W_BOARD].handle;
977 menuBarWidget = optList[W_MENU].handle;
978 dropMenu = optList[W_DROP].handle;
979 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
981 formWidget = XtParent(boardWidget);
982 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
983 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
984 XtGetValues(optList[W_WHITE].handle, args, 2);
985 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
986 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
987 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
988 XtGetValues(optList[W_PAUSE].handle, args, 2);
992 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
993 // not need to go into InitDrawingSizes().
998 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1000 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
1001 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
1002 mainwindowIcon = WhiteIcon;
1003 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1007 * Create a cursor for the board widget.
1010 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1011 XChangeWindowAttributes(xDisplay, xBoardWindow,
1012 CWCursor, &window_attributes);
1016 * Inhibit shell resizing.
1019 shellArgs[0].value = (XtArgVal) &w;
1020 shellArgs[1].value = (XtArgVal) &h;
1021 XtGetValues(shellWidget, shellArgs, 2);
1022 shellArgs[4].value = shellArgs[2].value = w;
1023 shellArgs[5].value = shellArgs[3].value = h;
1024 // XtSetValues(shellWidget, &shellArgs[2], 4);
1028 gtk_widget_get_allocation(shells[BoardWindow], &a);
1029 w = a.width; h = a.height;
1030 //printf("start size (%d,%d), %dx%d\n", a.x, a.y, w, h);
1031 gtk_widget_get_allocation(boardWidget, &a);
1032 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1033 marginH = h - a.height - 25; // subtract 25, because GTK seems to insert this amount of extra empty space
1034 gtk_window_resize(GTK_WINDOW(shellWidget), marginW + boardWidth, marginH + boardHeight);
1035 //printf("margins h=%d v=%d\n", marginW, marginH);
1041 if(appData.logoSize)
1042 { // locate and read user logo
1044 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1045 ASSIGN(userLogo, buf);
1048 if (appData.animate || appData.animateDragging)
1051 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1052 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1054 /* [AS] Restore layout */
1055 if( wpMoveHistory.visible ) {
1059 if( wpEvalGraph.visible )
1064 if( wpEngineOutput.visible ) {
1065 EngineOutputPopUp();
1070 if (errorExitStatus == -1) {
1071 if (appData.icsActive) {
1072 /* We now wait until we see "login:" from the ICS before
1073 sending the logon script (problems with timestamp otherwise) */
1074 /*ICSInitScript();*/
1075 if (appData.icsInputBox) ICSInputBoxPopUp();
1079 signal(SIGWINCH, TermSizeSigHandler);
1081 signal(SIGINT, IntSigHandler);
1082 signal(SIGTERM, IntSigHandler);
1083 if (*appData.cmailGameName != NULLCHAR) {
1084 signal(SIGUSR1, CmailSigHandler);
1088 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1091 // XtSetKeyboardFocus(shellWidget, formWidget);
1093 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1096 /* check for GTK events and process them */
1099 gtk_main_iteration();
1102 if (appData.debugMode) fclose(debugFP); // [DM] debug
1107 TermSizeSigHandler (int sig)
1113 IntSigHandler (int sig)
1119 CmailSigHandler (int sig)
1124 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1126 /* Activate call-back function CmailSigHandlerCallBack() */
1127 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1129 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1133 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1136 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1138 /**** end signal code ****/
1141 #define Abs(n) ((n)<0 ? -(n) : (n))
1145 InsertPxlSize (char *pattern, int targetPxlSize)
1147 char *base_fnt_lst, strInt[12], *p, *q;
1148 int alternatives, i, len, strIntLen;
1151 * Replace the "*" (if present) in the pixel-size slot of each
1152 * alternative with the targetPxlSize.
1156 while ((p = strchr(p, ',')) != NULL) {
1160 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1161 strIntLen = strlen(strInt);
1162 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1166 while (alternatives--) {
1167 char *comma = strchr(p, ',');
1168 for (i=0; i<14; i++) {
1169 char *hyphen = strchr(p, '-');
1171 if (comma && hyphen > comma) break;
1172 len = hyphen + 1 - p;
1173 if (i == 7 && *p == '*' && len == 2) {
1175 memcpy(q, strInt, strIntLen);
1185 len = comma + 1 - p;
1192 return base_fnt_lst;
1197 CreateFontSet (char *base_fnt_lst)
1200 char **missing_list;
1204 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1205 &missing_list, &missing_count, &def_string);
1206 if (appData.debugMode) {
1208 XFontStruct **font_struct_list;
1209 char **font_name_list;
1210 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1212 fprintf(debugFP, " got list %s, locale %s\n",
1213 XBaseFontNameListOfFontSet(fntSet),
1214 XLocaleOfFontSet(fntSet));
1215 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1216 for (i = 0; i < count; i++) {
1217 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1220 for (i = 0; i < missing_count; i++) {
1221 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1224 if (fntSet == NULL) {
1225 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1231 #else // not ENABLE_NLS
1233 * Find a font that matches "pattern" that is as close as
1234 * possible to the targetPxlSize. Prefer fonts that are k
1235 * pixels smaller to fonts that are k pixels larger. The
1236 * pattern must be in the X Consortium standard format,
1237 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1238 * The return value should be freed with XtFree when no
1242 FindFont (char *pattern, int targetPxlSize)
1244 char **fonts, *p, *best, *scalable, *scalableTail;
1245 int i, j, nfonts, minerr, err, pxlSize;
1248 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1250 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1251 programName, pattern);
1258 for (i=0; i<nfonts; i++) {
1261 if (*p != '-') continue;
1263 if (*p == NULLCHAR) break;
1264 if (*p++ == '-') j++;
1266 if (j < 7) continue;
1269 scalable = fonts[i];
1272 err = pxlSize - targetPxlSize;
1273 if (Abs(err) < Abs(minerr) ||
1274 (minerr > 0 && err < 0 && -err == minerr)) {
1280 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1281 /* If the error is too big and there is a scalable font,
1282 use the scalable font. */
1283 int headlen = scalableTail - scalable;
1284 p = (char *) XtMalloc(strlen(scalable) + 10);
1285 while (isdigit(*scalableTail)) scalableTail++;
1286 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1288 p = (char *) XtMalloc(strlen(best) + 2);
1289 safeStrCpy(p, best, strlen(best)+1 );
1291 if (appData.debugMode) {
1292 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1293 pattern, targetPxlSize, p);
1295 XFreeFontNames(fonts);
1302 EnableNamedMenuItem (char *menuRef, int state)
1304 MenuItem *item = MenuNameToItem(menuRef);
1306 if(item) gtk_widget_set_sensitive(item->handle, state);
1310 EnableButtonBar (int state)
1313 XtSetSensitive(optList[W_BUTTON].handle, state);
1319 SetMenuEnables (Enables *enab)
1321 while (enab->name != NULL) {
1322 EnableNamedMenuItem(enab->name, enab->value);
1327 gboolean KeyPressProc(window, eventkey, data)
1329 GdkEventKey *eventkey;
1333 MoveTypeInProc(eventkey); // pop up for typed in moves
1336 /* check for other key values */
1337 switch(eventkey->keyval) {
1349 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1350 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1352 if(*nprms == 0) return;
1353 item = MenuNameToItem(prms[0]);
1354 if(item) ((MenuProc *) item->proc) ();
1368 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1369 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1370 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1371 dmEnables[i].piece);
1372 XtSetSensitive(entry, p != NULL || !appData.testLegality
1373 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1374 && !appData.icsActive));
1376 while (p && *p++ == dmEnables[i].piece) count++;
1377 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1379 XtSetArg(args[j], XtNlabel, label); j++;
1380 XtSetValues(entry, args, j);
1386 do_flash_delay (unsigned long msec)
1392 FlashDelay (int flash_delay)
1394 if(flash_delay) do_flash_delay(flash_delay);
1398 Fraction (int x, int start, int stop)
1400 double f = ((double) x - start)/(stop - start);
1401 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1405 static WindowPlacement wpNew;
1408 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1410 int touch=0, fudge = 2, f = 2;
1411 GetActualPlacement(sh, wp);
1412 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1413 if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x) < fudge) touch = 2; else // left touch
1414 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1415 if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y) < fudge) touch = 4; // top touch
1416 //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);
1417 if(!touch ) return; // only windows that touch co-move
1418 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1419 int heightInc = wpNew.height - wpMain.height;
1420 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1421 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1422 wp->y += fracTop * heightInc;
1423 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1425 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1427 wp->height += heightInc;
1428 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1429 int widthInc = wpNew.width - wpMain.width;
1430 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1431 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1432 wp->y += fracLeft * widthInc;
1433 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1435 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1437 wp->width += widthInc;
1439 wp->x += wpNew.x - wpMain.x;
1440 wp->y += wpNew.y - wpMain.y;
1441 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1442 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1444 XtSetArg(args[j], XtNx, wp->x); j++;
1445 XtSetArg(args[j], XtNy, wp->y); j++;
1446 XtSetValues(sh, args, j);
1448 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1449 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1450 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1454 ReSize (WindowPlacement *wp)
1456 int sqx, sqy, w, h, lg = lineGap;
1457 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1458 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1459 sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1460 if(sqy < sqx) sqx = sqy;
1461 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1462 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1463 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1464 sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1465 if(sqy < sqx) sqx = sqy;
1467 if(sqx != squareSize) {
1468 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1469 squareSize = sqx; // adopt new square size
1470 CreatePNGPieces(); // make newly scaled pieces
1471 InitDrawingSizes(0, 0); // creates grid etc.
1472 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1473 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1474 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1475 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1476 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1479 static guint delayedDragTag = 0;
1488 // GetActualPlacement(shellWidget, &wpNew);
1489 //printf("drag proc (%d,%d) %dx%d\n", wpNew.x, wpNew.y, wpNew.width, wpNew.height);
1490 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1491 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1492 busy = 0; return; // false alarm
1495 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1496 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1497 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1498 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1500 DrawPosition(True, NULL);
1501 if(delayedDragTag) g_source_remove(delayedDragTag);
1502 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1509 //printf("old timr = %d\n", delayedDragTag);
1510 if(delayedDragTag) g_source_remove(delayedDragTag);
1511 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1512 //printf("new timr = %d\n", delayedDragTag);
1516 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1518 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1520 wpNew.x = event->configure.x;
1521 wpNew.y = event->configure.y;
1522 wpNew.width = event->configure.width;
1523 wpNew.height = event->configure.height;
1524 if(appData.useStickyWindows)
1525 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1531 /* Disable all user input other than deleting the window */
1532 static int frozen = 0;
1538 /* Grab by a widget that doesn't accept input */
1539 gtk_grab_add(optList[W_MESSG].handle);
1543 /* Undo a FreezeUI */
1547 if (!frozen) return;
1548 gtk_grab_remove(optList[W_MESSG].handle);
1555 static int oldPausing = FALSE;
1556 static GameMode oldmode = (GameMode) -1;
1558 if (!boardWidget) return;
1560 if (pausing != oldPausing) {
1561 oldPausing = pausing;
1562 MarkMenuItem("Mode.Pause", pausing);
1564 if (appData.showButtonBar) {
1565 /* Always toggle, don't set. Previous code messes up when
1566 invoked while the button is pressed, as releasing it
1567 toggles the state again. */
1569 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1570 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1574 wname = ModeToWidgetName(oldmode);
1575 if (wname != NULL) {
1576 MarkMenuItem(wname, False);
1578 wname = ModeToWidgetName(gameMode);
1579 if (wname != NULL) {
1580 MarkMenuItem(wname, True);
1583 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1585 /* Maybe all the enables should be handled here, not just this one */
1586 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1588 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1593 * Button/menu procedures
1596 void CopyFileToClipboard(gchar *filename)
1598 gchar *selection_tmp;
1602 FILE* f = fopen(filename, "r");
1605 if (f == NULL) return;
1609 selection_tmp = g_try_malloc(len + 1);
1610 if (selection_tmp == NULL) {
1611 printf("Malloc failed in CopyFileToClipboard\n");
1614 count = fread(selection_tmp, 1, len, f);
1617 g_free(selection_tmp);
1620 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1622 // copy selection_tmp to clipboard
1623 GdkDisplay *gdisp = gdk_display_get_default();
1625 g_free(selection_tmp);
1628 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1629 gtk_clipboard_set_text(cb, selection_tmp, -1);
1630 g_free(selection_tmp);
1634 CopySomething (char *src)
1636 GdkDisplay *gdisp = gdk_display_get_default();
1638 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1639 if (gdisp == NULL) return;
1640 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1641 gtk_clipboard_set_text(cb, src, -1);
1645 PastePositionProc ()
1647 GdkDisplay *gdisp = gdk_display_get_default();
1651 if (gdisp == NULL) return;
1652 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1653 fenstr = gtk_clipboard_wait_for_text(cb);
1654 if (fenstr==NULL) return; // nothing had been selected to copy
1655 EditPositionPasteFEN(fenstr);
1667 // get game from clipboard
1668 GdkDisplay *gdisp = gdk_display_get_default();
1669 if (gdisp == NULL) return;
1670 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1671 text = gtk_clipboard_wait_for_text(cb);
1672 if (text == NULL) return; // nothing to paste
1675 // write to temp file
1676 if (text == NULL || len == 0) {
1677 return; //nothing to paste
1679 f = fopen(gamePasteFilename, "w");
1681 DisplayError(_("Can't open temp file"), errno);
1684 fwrite(text, 1, len, f);
1688 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1695 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1701 void MoveTypeInProc(eventkey)
1702 GdkEventKey *eventkey;
1706 // ingnore if ctrl or alt is pressed
1707 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
1711 buf[0]=eventkey->keyval;
1719 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1721 if (!TempBackwardActive) {
1722 TempBackwardActive = True;
1728 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1730 /* Check to see if triggered by a key release event for a repeating key.
1731 * If so the next queued event will be a key press of the same key at the same time */
1732 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1734 XPeekEvent(xDisplay, &next);
1735 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1736 next.xkey.keycode == event->xkey.keycode)
1740 TempBackwardActive = False;
1744 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1745 { // called as key binding
1748 if (nprms && *nprms > 0)
1752 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
1759 { // called from menu
1761 ManInner(NULL, NULL, NULL, NULL);
1766 SetWindowTitle (char *text, char *title, char *icon)
1771 if (appData.titleInWindow) {
1773 XtSetArg(args[i], XtNlabel, text); i++;
1774 XtSetValues(titleWidget, args, i);
1777 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1778 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1779 XtSetValues(shellWidget, args, i);
1780 XSync(xDisplay, False);
1782 if (appData.titleInWindow) {
1783 SetWidgetLabel(titleWidget, text);
1785 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1790 DisplayIcsInteractionTitle (String message)
1793 if (oldICSInteractionTitle == NULL) {
1794 /* Magic to find the old window title, adapted from vim */
1795 char *wina = getenv("WINDOWID");
1797 Window win = (Window) atoi(wina);
1798 Window root, parent, *children;
1799 unsigned int nchildren;
1800 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1802 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1803 if (!XQueryTree(xDisplay, win, &root, &parent,
1804 &children, &nchildren)) break;
1805 if (children) XFree((void *)children);
1806 if (parent == root || parent == 0) break;
1809 XSetErrorHandler(oldHandler);
1811 if (oldICSInteractionTitle == NULL) {
1812 oldICSInteractionTitle = "xterm";
1815 printf("\033]0;%s\007", message);
1822 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1824 GtkWidget *w = (GtkWidget *) opt->handle;
1831 strcpy(bgcolor, "black");
1832 strcpy(fgcolor, "white");
1834 strcpy(bgcolor, "white");
1835 strcpy(fgcolor, "black");
1838 appData.lowTimeWarning &&
1839 (timer / 1000) < appData.icsAlarmTime) {
1840 strcpy(fgcolor, appData.lowTimeWarningColor);
1843 gdk_color_parse( bgcolor, &col );
1844 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1846 if (appData.clockMode) {
1847 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1848 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1850 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
1851 bgcolor, fgcolor, color);
1853 gtk_label_set_markup(GTK_LABEL(w), markup);
1857 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1860 SetClockIcon (int color)
1862 GdkPixbuf *pm = *clockIcons[color];
1863 if (mainwindowIcon != pm) {
1864 mainwindowIcon = pm;
1865 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1869 #define INPUT_SOURCE_BUF_SIZE 8192
1878 char buf[INPUT_SOURCE_BUF_SIZE];
1883 DoInputCallback(io, cond, data)
1888 /* read input from one of the input source (for example a chess program, ICS, etc).
1889 * and call a function that will handle the input
1896 /* All information (callback function, file descriptor, etc) is
1897 * saved in an InputSource structure
1899 InputSource *is = (InputSource *) data;
1901 if (is->lineByLine) {
1902 count = read(is->fd, is->unused,
1903 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1905 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1908 is->unused += count;
1910 /* break input into lines and call the callback function on each
1913 while (p < is->unused) {
1914 q = memchr(p, '\n', is->unused - p);
1915 if (q == NULL) break;
1917 (is->func)(is, is->closure, p, q - p, 0);
1920 /* remember not yet used part of the buffer */
1922 while (p < is->unused) {
1927 /* read maximum length of input buffer and send the whole buffer
1928 * to the callback function
1930 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1935 (is->func)(is, is->closure, is->buf, count, error);
1937 return True; // Must return true or the watch will be removed
1940 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1947 GIOChannel *channel;
1948 ChildProc *cp = (ChildProc *) pr;
1950 is = (InputSource *) calloc(1, sizeof(InputSource));
1951 is->lineByLine = lineByLine;
1955 is->fd = fileno(stdin);
1957 is->kind = cp->kind;
1958 is->fd = cp->fdFrom;
1961 is->unused = is->buf;
1965 /* GTK-TODO: will this work on windows?*/
1967 channel = g_io_channel_unix_new(is->fd);
1968 g_io_channel_set_close_on_unref (channel, TRUE);
1969 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1971 is->closure = closure;
1972 return (InputSourceRef) is;
1977 RemoveInputSource(isr)
1980 InputSource *is = (InputSource *) isr;
1982 if (is->sid == 0) return;
1983 g_source_remove(is->sid);
1990 static Boolean frameWaiting;
1993 FrameAlarm (int sig)
1995 frameWaiting = False;
1996 /* In case System-V style signals. Needed?? */
1997 signal(SIGALRM, FrameAlarm);
2001 FrameDelay (int time)
2003 struct itimerval delay;
2006 frameWaiting = True;
2007 signal(SIGALRM, FrameAlarm);
2008 delay.it_interval.tv_sec =
2009 delay.it_value.tv_sec = time / 1000;
2010 delay.it_interval.tv_usec =
2011 delay.it_value.tv_usec = (time % 1000) * 1000;
2012 setitimer(ITIMER_REAL, &delay, NULL);
2013 while (frameWaiting) pause();
2014 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2015 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2016 setitimer(ITIMER_REAL, &delay, NULL);
2023 FrameDelay (int time)
2026 XSync(xDisplay, False);
2028 // gtk_main_iteration_do(False);
2031 usleep(time * 1000);
2037 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2039 char buf[MSG_SIZ], *logoName = buf;
2040 if(appData.logo[n][0]) {
2041 logoName = appData.logo[n];
2042 } else if(appData.autoLogo) {
2043 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2044 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2045 } else if(appData.directory[n] && appData.directory[n][0]) {
2046 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2050 { ASSIGN(cps->programLogo, logoName); }
2054 UpdateLogos (int displ)
2056 if(optList[W_WHITE-1].handle == NULL) return;
2057 LoadLogo(&first, 0, 0);
2058 LoadLogo(&second, 1, appData.icsActive);
2059 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2063 void FileNamePopUpGTK(label, def, filter, proc, pathFlag, openMode, name, fp)
2074 GtkFileFilter *gtkfilter;
2075 GtkFileFilter *gtkfilter_all;
2077 char fileext[10] = "";
2078 char *result = NULL;
2081 /* make a copy of the filter string, so that strtok can work with it*/
2082 cp = strndup(filter,strlen(filter));
2084 /* add filters for file extensions */
2085 gtkfilter = gtk_file_filter_new();
2086 gtkfilter_all = gtk_file_filter_new();
2088 /* one filter to show everything */
2089 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2090 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2092 /* add filter if present */
2093 result = strtok(cp, space);
2094 while( result != NULL ) {
2095 snprintf(fileext,10,"*%s",result);
2096 result = strtok( NULL, space );
2097 gtk_file_filter_add_pattern(gtkfilter, fileext);
2100 /* second filter to only show what's useful */
2101 gtk_file_filter_set_name (gtkfilter,filter);
2103 if (openMode[0] == 'r')
2105 dialog = gtk_file_chooser_dialog_new (label,
2107 GTK_FILE_CHOOSER_ACTION_OPEN,
2108 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2109 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2114 dialog = gtk_file_chooser_dialog_new (label,
2116 GTK_FILE_CHOOSER_ACTION_SAVE,
2117 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2118 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2120 /* add filename suggestions */
2121 if (strlen(def) > 0 )
2122 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2124 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2128 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2129 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2130 /* activate filter */
2131 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2133 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2138 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2141 f = fopen(filename, openMode);
2144 DisplayError(_("Failed to open file"), errno);
2148 /* TODO add indec */
2150 ASSIGN(*name, filename);
2151 ScheduleDelayedEvent(DelayedLoad, 50);
2156 gtk_widget_destroy (dialog);