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 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
199 Boolean TempBackwardActive = False;
200 void DisplayMove P((int moveNumber));
201 void ICSInitScript P((void));
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()",
371 /* Max possible square size */
372 #define MAXSQSIZE 256
374 static int xpm_avail[MAXSQSIZE];
380 gtk_window_present(GTK_WINDOW(mainwindow));
383 //---------------------------------------------------------------------------------------------------------
384 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
387 #define CW_USEDEFAULT (1<<31)
388 #define ICS_TEXT_MENU_SIZE 90
389 #define DEBUG_FILE "xboard.debug"
390 #define SetCurrentDirectory chdir
391 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
395 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
398 // front-end part of option handling
400 // [HGM] This platform-dependent table provides the location for storing the color info
401 extern char *crWhite, * crBlack;
405 &appData.whitePieceColor,
406 &appData.blackPieceColor,
407 &appData.lightSquareColor,
408 &appData.darkSquareColor,
409 &appData.highlightSquareColor,
410 &appData.premoveHighlightColor,
411 &appData.lowTimeWarningColor,
422 // [HGM] font: keep a font for each square size, even non-stndard ones
425 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
426 char *fontTable[NUM_FONTS][MAX_SIZE];
429 ParseFont (char *name, int number)
430 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
432 if(sscanf(name, "size%d:", &size)) {
433 // [HGM] font: font is meant for specific boardSize (likely from settings file);
434 // defer processing it until we know if it matches our board size
435 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
436 fontTable[number][size] = strdup(strchr(name, ':')+1);
437 fontValid[number][size] = True;
442 case 0: // CLOCK_FONT
443 appData.clockFont = strdup(name);
445 case 1: // MESSAGE_FONT
446 appData.font = strdup(name);
448 case 2: // COORD_FONT
449 appData.coordFont = strdup(name);
454 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
459 { // only 2 fonts currently
460 appData.clockFont = CLOCK_FONT_NAME;
461 appData.coordFont = COORD_FONT_NAME;
462 appData.font = DEFAULT_FONT_NAME;
467 { // no-op, until we identify the code for this already in XBoard and move it here
471 ParseColor (int n, char *name)
472 { // in XBoard, just copy the color-name string
473 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
477 ParseTextAttribs (ColorClass cc, char *s)
479 (&appData.colorShout)[cc] = strdup(s);
483 ParseBoardSize (void *addr, char *name)
485 appData.boardSize = strdup(name);
490 { // In XBoard the sound-playing program takes care of obtaining the actual sound
494 SetCommPortDefaults ()
495 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
498 // [HGM] args: these three cases taken out to stay in front-end
500 SaveFontArg (FILE *f, ArgDescriptor *ad)
503 int i, n = (int)(intptr_t)ad->argLoc;
505 case 0: // CLOCK_FONT
506 name = appData.clockFont;
508 case 1: // MESSAGE_FONT
511 case 2: // COORD_FONT
512 name = appData.coordFont;
517 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
518 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
519 fontTable[n][squareSize] = strdup(name);
520 fontValid[n][squareSize] = True;
523 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
524 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
529 { // nothing to do, as the sounds are at all times represented by their text-string names already
533 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
534 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
535 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
539 SaveColor (FILE *f, ArgDescriptor *ad)
540 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
541 if(colorVariable[(int)(intptr_t)ad->argLoc])
542 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
546 SaveBoardSize (FILE *f, char *name, void *addr)
547 { // wrapper to shield back-end from BoardSize & sizeInfo
548 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
552 ParseCommPortSettings (char *s)
553 { // no such option in XBoard (yet)
559 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
563 gtk_widget_get_allocation(shell, &a);
567 wp->height = a.height;
568 printf("placement\n");
569 frameX = a.x; frameY = a.y; // remember to decide if windows touch
573 GetActualPlacement (Widget wg, WindowPlacement *wp)
575 XWindowAttributes winAt;
582 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
583 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
584 wp->x = rx - winAt.x;
585 wp->y = ry - winAt.y;
586 wp->height = winAt.height;
587 wp->width = winAt.width;
588 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
594 { // wrapper to shield use of window handles from back-end (make addressible by number?)
595 // In XBoard this will have to wait until awareness of window parameters is implemented
596 GetActualPlacement(shellWidget, &wpMain);
597 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
598 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
599 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
600 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
601 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
602 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
606 PrintCommPortSettings (FILE *f, char *name)
607 { // This option does not exist in XBoard
611 EnsureOnScreen (int *x, int *y, int minX, int minY)
618 { // [HGM] args: allows testing if main window is realized from back-end
619 return DialogExists(BoardWindow);
623 PopUpStartupDialog ()
624 { // start menu not implemented in XBoard
628 ConvertToLine (int argc, char **argv)
630 static char line[128*1024], buf[1024];
634 for(i=1; i<argc; i++)
636 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
637 && argv[i][0] != '{' )
638 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
640 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
641 strncat(line, buf, 128*1024 - strlen(line) - 1 );
644 line[strlen(line)-1] = NULLCHAR;
648 //--------------------------------------------------------------------------------------------
651 ResizeBoardWindow (int w, int h, int inhibit)
653 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
655 // gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
657 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
659 shellArgs[0].value = w;
660 shellArgs[1].value = h;
661 shellArgs[4].value = shellArgs[2].value = w;
662 shellArgs[5].value = shellArgs[3].value = h;
663 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
665 XSync(xDisplay, False);
671 { // dummy, as the GTK code does not make colors in advance
676 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
677 { // determine what fonts to use, and create them
682 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
683 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
684 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
685 appData.font = fontTable[MESSAGE_FONT][squareSize];
686 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
687 appData.coordFont = fontTable[COORD_FONT][squareSize];
690 appData.font = InsertPxlSize(appData.font, fontPxlSize);
691 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
692 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
693 fontSet = CreateFontSet(appData.font);
694 clockFontSet = CreateFontSet(appData.clockFont);
696 /* For the coordFont, use the 0th font of the fontset. */
697 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
698 XFontStruct **font_struct_list;
699 XFontSetExtents *fontSize;
700 char **font_name_list;
701 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
702 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
703 coordFontStruct = XQueryFont(xDisplay, coordFontID);
704 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
705 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
708 appData.font = FindFont(appData.font, fontPxlSize);
709 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
710 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
711 clockFontID = XLoadFont(xDisplay, appData.clockFont);
712 clockFontStruct = XQueryFont(xDisplay, clockFontID);
713 coordFontID = XLoadFont(xDisplay, appData.coordFont);
714 coordFontStruct = XQueryFont(xDisplay, coordFontID);
715 // textHeight in !NLS mode!
717 countFontID = coordFontID; // [HGM] holdings
718 countFontStruct = coordFontStruct;
720 xdb = XtDatabase(xDisplay);
722 XrmPutLineResource(&xdb, "*international: True");
723 vTo.size = sizeof(XFontSet);
724 vTo.addr = (XtPointer) &fontSet;
725 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
727 XrmPutStringResource(&xdb, "*font", appData.font);
738 case ArgInt: p = " N"; break;
739 case ArgString: p = " STR"; break;
740 case ArgBoolean: p = " TF"; break;
741 case ArgSettingsFilename:
742 case ArgFilename: p = " FILE"; break;
743 case ArgX: p = " Nx"; break;
744 case ArgY: p = " Ny"; break;
745 case ArgAttribs: p = " TEXTCOL"; break;
746 case ArgColor: p = " COL"; break;
747 case ArgFont: p = " FONT"; break;
748 case ArgBoardSize: p = " SIZE"; break;
749 case ArgFloat: p = " FLOAT"; break;
754 case ArgCommSettings:
765 ArgDescriptor *q, *p = argDescriptors+5;
766 printf("\nXBoard accepts the following options:\n"
767 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
768 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
769 " SIZE = board-size spec(s)\n"
770 " Within parentheses are short forms, or options to set to true or false.\n"
771 " Persistent options (saved in the settings file) are marked with *)\n\n");
773 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
774 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
775 if(p->save) strcat(buf+len, "*");
776 for(q=p+1; q->argLoc == p->argLoc; q++) {
777 if(q->argName[0] == '-') continue;
778 strcat(buf+len, q == p+1 ? " (" : " ");
779 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
781 if(q != p+1) strcat(buf+len, ")");
783 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
786 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
790 main (int argc, char **argv)
792 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
793 int boardWidth, boardHeight, w, h;
795 int forceMono = False;
796 GError *gtkerror=NULL;
798 srandom(time(0)); // [HGM] book: make random truly random
800 setbuf(stdout, NULL);
801 setbuf(stderr, NULL);
804 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
805 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
809 if(argc > 1 && !strcmp(argv[1], "--help" )) {
815 gtk_init (&argc, &argv);
817 programName = strrchr(argv[0], '/');
818 if (programName == NULL)
819 programName = argv[0];
824 // if (appData.debugMode) {
825 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
828 bindtextdomain(PACKAGE, LOCALEDIR);
832 appData.boardSize = "";
833 InitAppData(ConvertToLine(argc, argv));
835 if (p == NULL) p = "/tmp";
836 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
837 gameCopyFilename = (char*) malloc(i);
838 gamePasteFilename = (char*) malloc(i);
839 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
840 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
842 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
843 static char buf[MSG_SIZ];
844 EscapeExpand(buf, appData.firstInitString);
845 appData.firstInitString = strdup(buf);
846 EscapeExpand(buf, appData.secondInitString);
847 appData.secondInitString = strdup(buf);
848 EscapeExpand(buf, appData.firstComputerString);
849 appData.firstComputerString = strdup(buf);
850 EscapeExpand(buf, appData.secondComputerString);
851 appData.secondComputerString = strdup(buf);
854 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
857 if (chdir(chessDir) != 0) {
858 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
864 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
865 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
866 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
867 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
870 setbuf(debugFP, NULL);
874 if (appData.debugMode) {
875 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
879 /* [HGM,HR] make sure board size is acceptable */
880 if(appData.NrFiles > BOARD_FILES ||
881 appData.NrRanks > BOARD_RANKS )
882 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
885 /* This feature does not work; animation needs a rewrite */
886 appData.highlightDragging = FALSE;
890 gameInfo.variant = StringToVariant(appData.variant);
894 * determine size, based on supplied or remembered -size, or screen size
896 if (isdigit(appData.boardSize[0])) {
897 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
898 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
899 &fontPxlSize, &smallLayout, &tinyLayout);
901 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
902 programName, appData.boardSize);
906 /* Find some defaults; use the nearest known size */
907 SizeDefaults *szd, *nearest;
908 int distance = 99999;
909 nearest = szd = sizeDefaults;
910 while (szd->name != NULL) {
911 if (abs(szd->squareSize - squareSize) < distance) {
913 distance = abs(szd->squareSize - squareSize);
914 if (distance == 0) break;
918 if (i < 2) lineGap = nearest->lineGap;
919 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
920 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
921 if (i < 5) fontPxlSize = nearest->fontPxlSize;
922 if (i < 6) smallLayout = nearest->smallLayout;
923 if (i < 7) tinyLayout = nearest->tinyLayout;
926 SizeDefaults *szd = sizeDefaults;
927 if (*appData.boardSize == NULLCHAR) {
928 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
929 guint screenwidth = gdk_screen_get_width(screen);
930 guint screenheight = gdk_screen_get_height(screen);
931 while (screenwidth < szd->minScreenSize ||
932 screenheight < szd->minScreenSize) {
935 if (szd->name == NULL) szd--;
936 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
938 while (szd->name != NULL &&
939 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
940 if (szd->name == NULL) {
941 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
942 programName, appData.boardSize);
946 squareSize = szd->squareSize;
947 lineGap = szd->lineGap;
948 clockFontPxlSize = szd->clockFontPxlSize;
949 coordFontPxlSize = szd->coordFontPxlSize;
950 fontPxlSize = szd->fontPxlSize;
951 smallLayout = szd->smallLayout;
952 tinyLayout = szd->tinyLayout;
953 // [HGM] font: use defaults from settings file if available and not overruled
956 defaultLineGap = lineGap;
957 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
959 /* [HR] height treated separately (hacked) */
960 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
961 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
964 * Determine what fonts to use.
967 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
971 * Detect if there are not enough colors available and adapt.
974 if (DefaultDepth(xDisplay, xScreen) <= 2) {
975 appData.monoMode = True;
979 forceMono = MakeColors();
982 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
984 appData.monoMode = True;
987 ParseIcsTextColors();
993 layoutName = "tinyLayout";
994 } else if (smallLayout) {
995 layoutName = "smallLayout";
997 layoutName = "normalLayout";
1000 optList = BoardPopUp(squareSize, lineGap, (void*)
1010 InitDrawingHandle(optList + W_BOARD);
1011 shellWidget = shells[BoardWindow];
1012 currBoard = &optList[W_BOARD];
1013 boardWidget = optList[W_BOARD].handle;
1014 menuBarWidget = optList[W_MENU].handle;
1015 dropMenu = optList[W_DROP].handle;
1016 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1018 formWidget = XtParent(boardWidget);
1019 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1020 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1021 XtGetValues(optList[W_WHITE].handle, args, 2);
1022 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1023 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1024 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1025 XtGetValues(optList[W_PAUSE].handle, args, 2);
1029 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1030 // not need to go into InitDrawingSizes().
1035 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1037 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
1038 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
1039 mainwindowIcon = WhiteIcon;
1040 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1044 * Create a cursor for the board widget.
1047 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1048 XChangeWindowAttributes(xDisplay, xBoardWindow,
1049 CWCursor, &window_attributes);
1053 * Inhibit shell resizing.
1056 shellArgs[0].value = (XtArgVal) &w;
1057 shellArgs[1].value = (XtArgVal) &h;
1058 XtGetValues(shellWidget, shellArgs, 2);
1059 shellArgs[4].value = shellArgs[2].value = w;
1060 shellArgs[5].value = shellArgs[3].value = h;
1061 // XtSetValues(shellWidget, &shellArgs[2], 4);
1065 gtk_widget_get_allocation(shells[BoardWindow], &a);
1066 w = a.width; h = a.height;
1067 printf("start size (%d,%d), %dx%d\n", a.x, a.y, w, h);
1069 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1070 marginH = h - boardHeight;
1075 if(appData.logoSize)
1076 { // locate and read user logo
1078 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1079 ASSIGN(userLogo, buf);
1082 if (appData.animate || appData.animateDragging)
1085 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1086 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1088 /* [AS] Restore layout */
1089 if( wpMoveHistory.visible ) {
1093 if( wpEvalGraph.visible )
1098 if( wpEngineOutput.visible ) {
1099 EngineOutputPopUp();
1104 if (errorExitStatus == -1) {
1105 if (appData.icsActive) {
1106 /* We now wait until we see "login:" from the ICS before
1107 sending the logon script (problems with timestamp otherwise) */
1108 /*ICSInitScript();*/
1109 if (appData.icsInputBox) ICSInputBoxPopUp();
1113 signal(SIGWINCH, TermSizeSigHandler);
1115 signal(SIGINT, IntSigHandler);
1116 signal(SIGTERM, IntSigHandler);
1117 if (*appData.cmailGameName != NULLCHAR) {
1118 signal(SIGUSR1, CmailSigHandler);
1122 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1125 // XtSetKeyboardFocus(shellWidget, formWidget);
1127 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1130 /* check for GTK events and process them */
1133 gtk_main_iteration();
1136 if (appData.debugMode) fclose(debugFP); // [DM] debug
1141 TermSizeSigHandler (int sig)
1147 IntSigHandler (int sig)
1153 CmailSigHandler (int sig)
1158 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1160 /* Activate call-back function CmailSigHandlerCallBack() */
1161 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1163 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1167 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1170 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1172 /**** end signal code ****/
1175 #define Abs(n) ((n)<0 ? -(n) : (n))
1179 InsertPxlSize (char *pattern, int targetPxlSize)
1181 char *base_fnt_lst, strInt[12], *p, *q;
1182 int alternatives, i, len, strIntLen;
1185 * Replace the "*" (if present) in the pixel-size slot of each
1186 * alternative with the targetPxlSize.
1190 while ((p = strchr(p, ',')) != NULL) {
1194 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1195 strIntLen = strlen(strInt);
1196 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1200 while (alternatives--) {
1201 char *comma = strchr(p, ',');
1202 for (i=0; i<14; i++) {
1203 char *hyphen = strchr(p, '-');
1205 if (comma && hyphen > comma) break;
1206 len = hyphen + 1 - p;
1207 if (i == 7 && *p == '*' && len == 2) {
1209 memcpy(q, strInt, strIntLen);
1219 len = comma + 1 - p;
1226 return base_fnt_lst;
1231 CreateFontSet (char *base_fnt_lst)
1234 char **missing_list;
1238 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1239 &missing_list, &missing_count, &def_string);
1240 if (appData.debugMode) {
1242 XFontStruct **font_struct_list;
1243 char **font_name_list;
1244 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1246 fprintf(debugFP, " got list %s, locale %s\n",
1247 XBaseFontNameListOfFontSet(fntSet),
1248 XLocaleOfFontSet(fntSet));
1249 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1250 for (i = 0; i < count; i++) {
1251 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1254 for (i = 0; i < missing_count; i++) {
1255 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1258 if (fntSet == NULL) {
1259 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1265 #else // not ENABLE_NLS
1267 * Find a font that matches "pattern" that is as close as
1268 * possible to the targetPxlSize. Prefer fonts that are k
1269 * pixels smaller to fonts that are k pixels larger. The
1270 * pattern must be in the X Consortium standard format,
1271 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1272 * The return value should be freed with XtFree when no
1276 FindFont (char *pattern, int targetPxlSize)
1278 char **fonts, *p, *best, *scalable, *scalableTail;
1279 int i, j, nfonts, minerr, err, pxlSize;
1282 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1284 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1285 programName, pattern);
1292 for (i=0; i<nfonts; i++) {
1295 if (*p != '-') continue;
1297 if (*p == NULLCHAR) break;
1298 if (*p++ == '-') j++;
1300 if (j < 7) continue;
1303 scalable = fonts[i];
1306 err = pxlSize - targetPxlSize;
1307 if (Abs(err) < Abs(minerr) ||
1308 (minerr > 0 && err < 0 && -err == minerr)) {
1314 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1315 /* If the error is too big and there is a scalable font,
1316 use the scalable font. */
1317 int headlen = scalableTail - scalable;
1318 p = (char *) XtMalloc(strlen(scalable) + 10);
1319 while (isdigit(*scalableTail)) scalableTail++;
1320 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1322 p = (char *) XtMalloc(strlen(best) + 2);
1323 safeStrCpy(p, best, strlen(best)+1 );
1325 if (appData.debugMode) {
1326 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1327 pattern, targetPxlSize, p);
1329 XFreeFontNames(fonts);
1336 EnableNamedMenuItem (char *menuRef, int state)
1338 MenuItem *item = MenuNameToItem(menuRef);
1340 if(item) gtk_widget_set_sensitive(item->handle, state);
1344 EnableButtonBar (int state)
1347 XtSetSensitive(optList[W_BUTTON].handle, state);
1353 SetMenuEnables (Enables *enab)
1355 while (enab->name != NULL) {
1356 EnableNamedMenuItem(enab->name, enab->value);
1361 gboolean KeyPressProc(window, eventkey, data)
1363 GdkEventKey *eventkey;
1367 MoveTypeInProc(eventkey); // pop up for typed in moves
1370 /* check for other key values */
1371 switch(eventkey->keyval) {
1383 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1384 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1386 if(*nprms == 0) return;
1387 item = MenuNameToItem(prms[0]);
1388 if(item) ((MenuProc *) item->proc) ();
1402 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1403 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1404 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1405 dmEnables[i].piece);
1406 XtSetSensitive(entry, p != NULL || !appData.testLegality
1407 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1408 && !appData.icsActive));
1410 while (p && *p++ == dmEnables[i].piece) count++;
1411 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1413 XtSetArg(args[j], XtNlabel, label); j++;
1414 XtSetValues(entry, args, j);
1420 do_flash_delay (unsigned long msec)
1426 FlashDelay (int flash_delay)
1428 if(flash_delay) do_flash_delay(flash_delay);
1432 Fraction (int x, int start, int stop)
1434 double f = ((double) x - start)/(stop - start);
1435 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1439 static WindowPlacement wpNew;
1442 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1444 int j=0, touch=0, fudge = 2;
1445 GetActualPlacement(sh, wp);
1446 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1447 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1448 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1449 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1450 if(!touch ) return; // only windows that touch co-move
1451 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1452 int heightInc = wpNew.height - wpMain.height;
1453 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1454 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1455 wp->y += fracTop * heightInc;
1456 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1458 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1460 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1461 int widthInc = wpNew.width - wpMain.width;
1462 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1463 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1464 wp->y += fracLeft * widthInc;
1465 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1467 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1470 wp->x += wpNew.x - wpMain.x;
1471 wp->y += wpNew.y - wpMain.y;
1472 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1473 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1475 XtSetArg(args[j], XtNx, wp->x); j++;
1476 XtSetArg(args[j], XtNy, wp->y); j++;
1477 XtSetValues(sh, args, j);
1482 ReSize (WindowPlacement *wp)
1485 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1486 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1487 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1488 if(sqy < sqx) sqx = sqy;
1489 if(sqx != squareSize) {
1490 squareSize = sqx; // adopt new square size
1491 CreatePNGPieces(); // make newly scaled pieces
1492 InitDrawingSizes(0, 0); // creates grid etc.
1493 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1494 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1495 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1496 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1497 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1500 static guint delayedDragTag = 0;
1509 // GetActualPlacement(shellWidget, &wpNew);
1510 printf("drag proc (%d,%d) %dx%d\n", wpNew.x, wpNew.y, wpNew.width, wpNew.height);
1511 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1512 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1513 busy = 0; return; // false alarm
1516 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1517 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1518 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1519 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1521 DrawPosition(True, NULL);
1522 if(delayedDragTag) g_source_remove(delayedDragTag);
1523 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1530 printf("old timr = %d\n", delayedDragTag);
1531 if(delayedDragTag) g_source_remove(delayedDragTag);
1532 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1533 printf("new timr = %d\n", delayedDragTag);
1537 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1539 printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1541 wpNew.x = event->configure.x;
1542 wpNew.y = event->configure.y;
1543 wpNew.width = event->configure.width;
1544 wpNew.height = event->configure.height;
1545 if(appData.useStickyWindows)
1546 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1552 /* Disable all user input other than deleting the window */
1553 static int frozen = 0;
1559 /* Grab by a widget that doesn't accept input */
1560 gtk_grab_add(optList[W_MESSG].handle);
1564 /* Undo a FreezeUI */
1568 if (!frozen) return;
1569 gtk_grab_remove(optList[W_MESSG].handle);
1576 static int oldPausing = FALSE;
1577 static GameMode oldmode = (GameMode) -1;
1579 if (!boardWidget) return;
1581 if (pausing != oldPausing) {
1582 oldPausing = pausing;
1583 MarkMenuItem("Mode.Pause", pausing);
1585 if (appData.showButtonBar) {
1586 /* Always toggle, don't set. Previous code messes up when
1587 invoked while the button is pressed, as releasing it
1588 toggles the state again. */
1590 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1591 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1595 wname = ModeToWidgetName(oldmode);
1596 if (wname != NULL) {
1597 MarkMenuItem(wname, False);
1599 wname = ModeToWidgetName(gameMode);
1600 if (wname != NULL) {
1601 MarkMenuItem(wname, True);
1604 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1606 /* Maybe all the enables should be handled here, not just this one */
1607 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1609 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1614 * Button/menu procedures
1617 void CopyFileToClipboard(gchar *filename)
1619 gchar *selection_tmp;
1623 FILE* f = fopen(filename, "r");
1626 if (f == NULL) return;
1630 selection_tmp = g_try_malloc(len + 1);
1631 if (selection_tmp == NULL) {
1632 printf("Malloc failed in CopyFileToClipboard\n");
1635 count = fread(selection_tmp, 1, len, f);
1638 g_free(selection_tmp);
1641 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1643 // copy selection_tmp to clipboard
1644 GdkDisplay *gdisp = gdk_display_get_default();
1646 g_free(selection_tmp);
1649 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1650 gtk_clipboard_set_text(cb, selection_tmp, -1);
1651 g_free(selection_tmp);
1655 CopySomething (char *src)
1657 GdkDisplay *gdisp = gdk_display_get_default();
1659 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1660 if (gdisp == NULL) return;
1661 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1662 gtk_clipboard_set_text(cb, src, -1);
1666 PastePositionProc ()
1668 GdkDisplay *gdisp = gdk_display_get_default();
1672 if (gdisp == NULL) return;
1673 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1674 fenstr = gtk_clipboard_wait_for_text(cb);
1675 if (fenstr==NULL) return; // nothing had been selected to copy
1676 EditPositionPasteFEN(fenstr);
1688 // get game from clipboard
1689 GdkDisplay *gdisp = gdk_display_get_default();
1690 if (gdisp == NULL) return;
1691 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1692 text = gtk_clipboard_wait_for_text(cb);
1693 if (text == NULL) return; // nothing to paste
1696 // write to temp file
1697 if (text == NULL || len == 0) {
1698 return; //nothing to paste
1700 f = fopen(gamePasteFilename, "w");
1702 DisplayError(_("Can't open temp file"), errno);
1705 fwrite(text, 1, len, f);
1709 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1716 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1722 void MoveTypeInProc(eventkey)
1723 GdkEventKey *eventkey;
1727 // ingnore if ctrl or alt is pressed
1728 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
1732 buf[0]=eventkey->keyval;
1740 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1742 if (!TempBackwardActive) {
1743 TempBackwardActive = True;
1749 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1751 /* Check to see if triggered by a key release event for a repeating key.
1752 * If so the next queued event will be a key press of the same key at the same time */
1753 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1755 XPeekEvent(xDisplay, &next);
1756 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1757 next.xkey.keycode == event->xkey.keycode)
1761 TempBackwardActive = False;
1765 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1766 { // called as key binding
1769 if (nprms && *nprms > 0)
1773 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
1780 { // called from menu
1782 ManInner(NULL, NULL, NULL, NULL);
1787 SetWindowTitle (char *text, char *title, char *icon)
1792 if (appData.titleInWindow) {
1794 XtSetArg(args[i], XtNlabel, text); i++;
1795 XtSetValues(titleWidget, args, i);
1798 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1799 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1800 XtSetValues(shellWidget, args, i);
1801 XSync(xDisplay, False);
1803 if (appData.titleInWindow) {
1804 SetWidgetLabel(titleWidget, text);
1806 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1811 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
1817 DisplayIcsInteractionTitle (String message)
1820 if (oldICSInteractionTitle == NULL) {
1821 /* Magic to find the old window title, adapted from vim */
1822 char *wina = getenv("WINDOWID");
1824 Window win = (Window) atoi(wina);
1825 Window root, parent, *children;
1826 unsigned int nchildren;
1827 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1829 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1830 if (!XQueryTree(xDisplay, win, &root, &parent,
1831 &children, &nchildren)) break;
1832 if (children) XFree((void *)children);
1833 if (parent == root || parent == 0) break;
1836 XSetErrorHandler(oldHandler);
1838 if (oldICSInteractionTitle == NULL) {
1839 oldICSInteractionTitle = "xterm";
1842 printf("\033]0;%s\007", message);
1849 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1851 GtkWidget *w = (GtkWidget *) opt->handle;
1857 strcpy(bgcolor, "black");
1858 strcpy(fgcolor, "white");
1860 strcpy(bgcolor, "white");
1861 strcpy(fgcolor, "black");
1864 appData.lowTimeWarning &&
1865 (timer / 1000) < appData.icsAlarmTime) {
1866 strcpy(fgcolor, appData.lowTimeWarningColor);
1869 if (appData.clockMode) {
1870 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1871 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1873 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
1874 bgcolor, fgcolor, color);
1876 gtk_label_set_markup(GTK_LABEL(w), markup);
1880 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1883 SetClockIcon (int color)
1885 GdkPixbuf *pm = *clockIcons[color];
1886 if (mainwindowIcon != pm) {
1887 mainwindowIcon = pm;
1888 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1892 #define INPUT_SOURCE_BUF_SIZE 8192
1901 char buf[INPUT_SOURCE_BUF_SIZE];
1906 DoInputCallback(io, cond, data)
1911 /* read input from one of the input source (for example a chess program, ICS, etc).
1912 * and call a function that will handle the input
1919 /* All information (callback function, file descriptor, etc) is
1920 * saved in an InputSource structure
1922 InputSource *is = (InputSource *) data;
1924 if (is->lineByLine) {
1925 count = read(is->fd, is->unused,
1926 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1928 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1931 is->unused += count;
1933 /* break input into lines and call the callback function on each
1936 while (p < is->unused) {
1937 q = memchr(p, '\n', is->unused - p);
1938 if (q == NULL) break;
1940 (is->func)(is, is->closure, p, q - p, 0);
1943 /* remember not yet used part of the buffer */
1945 while (p < is->unused) {
1950 /* read maximum length of input buffer and send the whole buffer
1951 * to the callback function
1953 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1958 (is->func)(is, is->closure, is->buf, count, error);
1960 return True; // Must return true or the watch will be removed
1963 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1970 GIOChannel *channel;
1971 ChildProc *cp = (ChildProc *) pr;
1973 is = (InputSource *) calloc(1, sizeof(InputSource));
1974 is->lineByLine = lineByLine;
1978 is->fd = fileno(stdin);
1980 is->kind = cp->kind;
1981 is->fd = cp->fdFrom;
1984 is->unused = is->buf;
1988 /* GTK-TODO: will this work on windows?*/
1990 channel = g_io_channel_unix_new(is->fd);
1991 g_io_channel_set_close_on_unref (channel, TRUE);
1992 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1994 is->closure = closure;
1995 return (InputSourceRef) is;
2000 RemoveInputSource(isr)
2003 InputSource *is = (InputSource *) isr;
2005 if (is->sid == 0) return;
2006 g_source_remove(is->sid);
2013 static Boolean frameWaiting;
2016 FrameAlarm (int sig)
2018 frameWaiting = False;
2019 /* In case System-V style signals. Needed?? */
2020 signal(SIGALRM, FrameAlarm);
2024 FrameDelay (int time)
2026 struct itimerval delay;
2029 frameWaiting = True;
2030 signal(SIGALRM, FrameAlarm);
2031 delay.it_interval.tv_sec =
2032 delay.it_value.tv_sec = time / 1000;
2033 delay.it_interval.tv_usec =
2034 delay.it_value.tv_usec = (time % 1000) * 1000;
2035 setitimer(ITIMER_REAL, &delay, NULL);
2036 while (frameWaiting) pause();
2037 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2038 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2039 setitimer(ITIMER_REAL, &delay, NULL);
2046 FrameDelay (int time)
2049 XSync(xDisplay, False);
2051 // gtk_main_iteration_do(False);
2054 usleep(time * 1000);
2060 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2062 char buf[MSG_SIZ], *logoName = buf;
2063 if(appData.logo[n][0]) {
2064 logoName = appData.logo[n];
2065 } else if(appData.autoLogo) {
2066 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2067 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2068 } else if(appData.directory[n] && appData.directory[n][0]) {
2069 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2073 { ASSIGN(cps->programLogo, logoName); }
2077 UpdateLogos (int displ)
2079 if(optList[W_WHITE-1].handle == NULL) return;
2080 LoadLogo(&first, 0, 0);
2081 LoadLogo(&second, 1, appData.icsActive);
2082 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2086 void FileNamePopUpGTK(label, def, filter, proc, pathFlag, openMode, name, fp)
2097 GtkFileFilter *gtkfilter;
2098 GtkFileFilter *gtkfilter_all;
2100 char fileext[10] = "";
2101 char *result = NULL;
2104 /* make a copy of the filter string, so that strtok can work with it*/
2105 cp = strndup(filter,strlen(filter));
2107 /* add filters for file extensions */
2108 gtkfilter = gtk_file_filter_new();
2109 gtkfilter_all = gtk_file_filter_new();
2111 /* one filter to show everything */
2112 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2113 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2115 /* add filter if present */
2116 result = strtok(cp, space);
2117 while( result != NULL ) {
2118 snprintf(fileext,10,"*%s",result);
2119 result = strtok( NULL, space );
2120 gtk_file_filter_add_pattern(gtkfilter, fileext);
2123 /* second filter to only show what's useful */
2124 gtk_file_filter_set_name (gtkfilter,filter);
2126 if (openMode[0] == 'r')
2128 dialog = gtk_file_chooser_dialog_new (label,
2130 GTK_FILE_CHOOSER_ACTION_OPEN,
2131 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2132 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2137 dialog = gtk_file_chooser_dialog_new (label,
2139 GTK_FILE_CHOOSER_ACTION_SAVE,
2140 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2141 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2143 /* add filename suggestions */
2144 if (strlen(def) > 0 )
2145 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2147 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2151 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2152 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2153 /* activate filter */
2154 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2156 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2161 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2164 f = fopen(filename, openMode);
2167 DisplayError(_("Failed to open file"), errno);
2171 /* TODO add indec */
2173 ASSIGN(*name, filename);
2174 ScheduleDelayedEvent(DelayedLoad, 50);
2179 gtk_widget_destroy (dialog);