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, 2014 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"
171 # include <gtkmacintegration/gtkosxapplication.h>
172 // prevent pathname of positional file argument provided by OS X being be mistaken for option name
173 // (price is that we won't recognize Windows option format anymore).
176 // redefine some defaults
179 # undef SETTINGS_FILE
180 # define ICS_LOGON "Library/Preferences/XboardICS.conf"
181 # define DATADIR dataDir
182 # define SETTINGS_FILE masterSettings
183 # define SYNC_MENUBAR gtkosx_application_sync_menubar(theApp)
184 char dataDir[MSG_SIZ]; // for expanding ~~
185 char masterSettings[MSG_SIZ];
189 # define SYNC_MENUBAR
196 #define usleep(t) _sleep2(((t)+500)/1000)
200 # define _(s) gettext (s)
201 # define N_(s) gettext_noop (s)
207 int main P((int argc, char **argv));
208 RETSIGTYPE CmailSigHandler P((int sig));
209 RETSIGTYPE IntSigHandler P((int sig));
210 RETSIGTYPE TermSizeSigHandler P((int sig));
211 char *InsertPxlSize P((char *pattern, int targetPxlSize));
213 XFontSet CreateFontSet P((char *base_fnt_lst));
215 char *FindFont P((char *pattern, int targetPxlSize));
217 void DelayedDrag P((void));
218 void ICSInputBoxPopUp P((void));
219 void MoveTypeInProc P((GdkEventKey *eventkey));
220 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
221 Boolean TempBackwardActive = False;
222 void DisplayMove P((int moveNumber));
223 void update_ics_width P(());
224 int CopyMemoProc P(());
225 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
229 XFontSet fontSet, clockFontSet;
232 XFontStruct *clockFontStruct;
234 Font coordFontID, countFontID;
235 XFontStruct *coordFontStruct, *countFontStruct;
237 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
238 GtkWidget *mainwindow;
240 Option *optList; // contains all widgets of main window
243 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
246 static GdkPixbuf *mainwindowIcon=NULL;
247 static GdkPixbuf *WhiteIcon=NULL;
248 static GdkPixbuf *BlackIcon=NULL;
250 /* key board accelerators */
251 GtkAccelGroup *GtkAccelerators;
253 typedef unsigned int BoardSize;
255 Boolean chessProgram;
257 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
258 int smallLayout = 0, tinyLayout = 0,
259 marginW, marginH, // [HGM] for run-time resizing
260 fromX = -1, fromY = -1, toX, toY, commentUp = False,
261 errorExitStatus = -1, defaultLineGap;
263 Dimension textHeight;
265 char *chessDir, *programName, *programVersion;
266 Boolean alwaysOnTop = False;
267 char *icsTextMenuString;
269 char *firstChessProgramNames;
270 char *secondChessProgramNames;
272 WindowPlacement wpMain;
273 WindowPlacement wpConsole;
274 WindowPlacement wpComment;
275 WindowPlacement wpMoveHistory;
276 WindowPlacement wpEvalGraph;
277 WindowPlacement wpEngineOutput;
278 WindowPlacement wpGameList;
279 WindowPlacement wpTags;
280 WindowPlacement wpDualBoard;
282 /* This magic number is the number of intermediate frames used
283 in each half of the animation. For short moves it's reduced
284 by 1. The total number of frames will be factor * 2 + 1. */
287 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
294 DropMenuEnables dmEnables[] = {
303 XtResource clientResources[] = {
304 { "flashCount", "flashCount", XtRInt, sizeof(int),
305 XtOffset(AppDataPtr, flashCount), XtRImmediate,
306 (XtPointer) FLASH_COUNT },
310 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
311 char globalTranslations[] =
312 ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
313 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
314 :<KeyDown>Return: TempBackwardProc() \n \
315 :<KeyUp>Return: TempForwardProc() \n";
317 char ICSInputTranslations[] =
318 "<Key>Up: UpKeyProc() \n "
319 "<Key>Down: DownKeyProc() \n "
320 "<Key>Return: EnterKeyProc() \n";
322 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
323 // as the widget is destroyed before the up-click can call extend-end
324 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
327 String xboardResources[] = {
328 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
336 gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
339 //---------------------------------------------------------------------------------------------------------
340 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
343 #define CW_USEDEFAULT (1<<31)
344 #define ICS_TEXT_MENU_SIZE 90
345 #define DEBUG_FILE "xboard.debug"
346 #define SetCurrentDirectory chdir
347 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
351 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
354 // front-end part of option handling
356 // [HGM] This platform-dependent table provides the location for storing the color info
357 extern char *crWhite, * crBlack;
361 &appData.whitePieceColor,
362 &appData.blackPieceColor,
363 &appData.lightSquareColor,
364 &appData.darkSquareColor,
365 &appData.highlightSquareColor,
366 &appData.premoveHighlightColor,
367 &appData.lowTimeWarningColor,
378 // [HGM] font: keep a font for each square size, even non-stndard ones
381 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
382 char *fontTable[NUM_FONTS][MAX_SIZE];
385 ParseFont (char *name, int number)
386 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
388 if(sscanf(name, "size%d:", &size)) {
389 // [HGM] font: font is meant for specific boardSize (likely from settings file);
390 // defer processing it until we know if it matches our board size
391 if(!strstr(name, "-*-") && // ignore X-fonts
392 size >= 0 && size<MAX_SIZE) { // for now, fixed limit
393 fontTable[number][size] = strdup(strchr(name, ':')+1);
394 fontValid[number][size] = True;
399 case 0: // CLOCK_FONT
400 appData.clockFont = strdup(name);
402 case 1: // MESSAGE_FONT
403 appData.font = strdup(name);
405 case 2: // COORD_FONT
406 appData.coordFont = strdup(name);
409 appData.icsFont = strdup(name);
412 appData.tagsFont = strdup(name);
415 appData.commentFont = strdup(name);
417 case MOVEHISTORY_FONT:
418 appData.historyFont = strdup(name);
421 appData.gameListFont = strdup(name);
426 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
431 { // only 2 fonts currently
432 appData.clockFont = strdup(CLOCK_FONT_NAME);
433 appData.coordFont = strdup(COORD_FONT_NAME);
434 appData.font = strdup(DEFAULT_FONT_NAME);
435 appData.icsFont = strdup(CONSOLE_FONT_NAME);
436 appData.tagsFont = strdup(TAGS_FONT_NAME);
437 appData.commentFont = strdup(COMMENT_FONT_NAME);
438 appData.historyFont = strdup(HISTORY_FONT_NAME);
439 appData.gameListFont = strdup(GAMELIST_FONT_NAME);
444 { // no-op, until we identify the code for this already in XBoard and move it here
448 ParseColor (int n, char *name)
449 { // in XBoard, just copy the color-name string
450 if(colorVariable[n] && *name == '#') *(char**)colorVariable[n] = strdup(name);
456 return *(char**)colorVariable[n];
460 ParseTextAttribs (ColorClass cc, char *s)
462 (&appData.colorShout)[cc] = strdup(s);
466 ParseBoardSize (void *addr, char *name)
468 appData.boardSize = strdup(name);
473 { // In XBoard the sound-playing program takes care of obtaining the actual sound
477 SetCommPortDefaults ()
478 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
481 // [HGM] args: these three cases taken out to stay in front-end
483 SaveFontArg (FILE *f, ArgDescriptor *ad)
486 int i, n = (int)(intptr_t)ad->argLoc;
488 case 0: // CLOCK_FONT
489 name = appData.clockFont;
491 case 1: // MESSAGE_FONT
494 case 2: // COORD_FONT
495 name = appData.coordFont;
498 name = appData.icsFont;
501 name = appData.tagsFont;
504 name = appData.commentFont;
506 case MOVEHISTORY_FONT:
507 name = appData.historyFont;
510 name = appData.gameListFont;
515 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
516 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
517 fontTable[n][squareSize] = strdup(name);
518 fontValid[n][squareSize] = True;
521 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
522 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
527 { // nothing to do, as the sounds are at all times represented by their text-string names already
531 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
532 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
533 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
537 SaveColor (FILE *f, ArgDescriptor *ad)
538 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
539 if(colorVariable[(int)(intptr_t)ad->argLoc])
540 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
544 SaveBoardSize (FILE *f, char *name, void *addr)
545 { // wrapper to shield back-end from BoardSize & sizeInfo
546 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
550 ParseCommPortSettings (char *s)
551 { // no such option in XBoard (yet)
557 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
561 gtk_widget_get_allocation(shell, &a);
562 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
566 wp->height = a.height;
567 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
568 frameX = 3; frameY = 3; // remember to decide if windows touch
572 GetPlacement (DialogClass dlg, WindowPlacement *wp)
573 { // wrapper to shield back-end from widget type
574 if(shellUp[dlg]) GetActualPlacement(shells[dlg], wp);
579 { // wrapper to shield use of window handles from back-end (make addressible by number?)
580 // In XBoard this will have to wait until awareness of window parameters is implemented
581 GetActualPlacement(shellWidget, &wpMain);
582 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
583 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
584 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
585 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
586 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
587 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
588 GetPlacement(ChatDlg, &wpConsole); if(appData.icsActive) wpConsole.visible = shellUp[ChatDlg];
592 PrintCommPortSettings (FILE *f, char *name)
593 { // This option does not exist in XBoard
597 EnsureOnScreen (int *x, int *y, int minX, int minY)
604 { // [HGM] args: allows testing if main window is realized from back-end
605 return DialogExists(BoardWindow);
609 PopUpStartupDialog ()
610 { // start menu not implemented in XBoard
614 ConvertToLine (int argc, char **argv)
616 static char line[128*1024], buf[1024];
620 for(i=1; i<argc; i++)
622 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
623 && argv[i][0] != '{' )
624 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
626 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
627 strncat(line, buf, 128*1024 - strlen(line) - 1 );
630 line[strlen(line)-1] = NULLCHAR;
634 //--------------------------------------------------------------------------------------------
639 ResizeBoardWindow (int w, int h, int inhibit)
643 // if(clockKludge) return; // ignore as long as clock does not have final height
644 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
646 gtk_widget_get_allocation(shellWidget, &a);
647 marginW = a.width - bw;
648 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
649 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
650 h += marginH + a.height + 1;
651 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
656 { // dummy, as the GTK code does not make colors in advance
661 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
662 { // determine what fonts to use, and create them
664 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
665 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
666 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
667 appData.font = fontTable[MESSAGE_FONT][squareSize];
668 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
669 appData.coordFont = fontTable[COORD_FONT][squareSize];
670 if(!fontIsSet[CONSOLE_FONT] && fontValid[CONSOLE_FONT][squareSize])
671 appData.icsFont = fontTable[CONSOLE_FONT][squareSize];
672 if(!fontIsSet[EDITTAGS_FONT] && fontValid[EDITTAGS_FONT][squareSize])
673 appData.tagsFont = fontTable[EDITTAGS_FONT][squareSize];
674 if(!fontIsSet[COMMENT_FONT] && fontValid[COMMENT_FONT][squareSize])
675 appData.commentFont = fontTable[COMMENT_FONT][squareSize];
676 if(!fontIsSet[MOVEHISTORY_FONT] && fontValid[MOVEHISTORY_FONT][squareSize])
677 appData.historyFont = fontTable[MOVEHISTORY_FONT][squareSize];
678 if(!fontIsSet[GAMELIST_FONT] && fontValid[GAMELIST_FONT][squareSize])
679 appData.gameListFont = fontTable[GAMELIST_FONT][squareSize];
681 appData.font = InsertPxlSize(appData.font, coordFontPxlSize);
682 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
683 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
684 appData.icsFont = InsertPxlSize(appData.icsFont, coordFontPxlSize);
685 appData.tagsFont = InsertPxlSize(appData.tagsFont, coordFontPxlSize);
686 appData.commentFont = InsertPxlSize(appData.commentFont, coordFontPxlSize);
687 appData.historyFont = InsertPxlSize(appData.historyFont, coordFontPxlSize);
688 appData.gameListFont = InsertPxlSize(appData.gameListFont, coordFontPxlSize);
694 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
695 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
696 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
697 appData.font = fontTable[MESSAGE_FONT][squareSize];
698 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
699 appData.coordFont = fontTable[COORD_FONT][squareSize];
702 appData.font = InsertPxlSize(appData.font, fontPxlSize);
703 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
704 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
705 fontSet = CreateFontSet(appData.font);
706 clockFontSet = CreateFontSet(appData.clockFont);
708 /* For the coordFont, use the 0th font of the fontset. */
709 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
710 XFontStruct **font_struct_list;
711 XFontSetExtents *fontSize;
712 char **font_name_list;
713 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
714 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
715 coordFontStruct = XQueryFont(xDisplay, coordFontID);
716 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
717 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
720 appData.font = FindFont(appData.font, fontPxlSize);
721 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
722 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
723 clockFontID = XLoadFont(xDisplay, appData.clockFont);
724 clockFontStruct = XQueryFont(xDisplay, clockFontID);
725 coordFontID = XLoadFont(xDisplay, appData.coordFont);
726 coordFontStruct = XQueryFont(xDisplay, coordFontID);
727 // textHeight in !NLS mode!
729 countFontID = coordFontID; // [HGM] holdings
730 countFontStruct = coordFontStruct;
732 xdb = XtDatabase(xDisplay);
734 XrmPutLineResource(&xdb, "*international: True");
735 vTo.size = sizeof(XFontSet);
736 vTo.addr = (XtPointer) &fontSet;
737 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
739 XrmPutStringResource(&xdb, "*font", appData.font);
750 case ArgInt: p = " N"; break;
751 case ArgString: p = " STR"; break;
752 case ArgBoolean: p = " TF"; break;
753 case ArgSettingsFilename:
754 case ArgBackupSettingsFile:
755 case ArgFilename: p = " FILE"; break;
756 case ArgX: p = " Nx"; break;
757 case ArgY: p = " Ny"; break;
758 case ArgAttribs: p = " TEXTCOL"; break;
759 case ArgColor: p = " COL"; break;
760 case ArgFont: p = " FONT"; break;
761 case ArgBoardSize: p = " SIZE"; break;
762 case ArgFloat: p = " FLOAT"; break;
767 case ArgCommSettings:
779 ArgDescriptor *q, *p = argDescriptors+5;
780 printf("\nXBoard accepts the following options:\n"
781 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
782 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
783 " SIZE = board-size spec(s)\n"
784 " Within parentheses are short forms, or options to set to true or false.\n"
785 " Persistent options (saved in the settings file) are marked with *)\n\n");
787 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
788 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
789 if(p->save) strcat(buf+len, "*");
790 for(q=p+1; q->argLoc == p->argLoc; q++) {
791 if(q->argName[0] == '-') continue;
792 strcat(buf+len, q == p+1 ? " (" : " ");
793 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
795 if(q != p+1) strcat(buf+len, ")");
797 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
800 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
804 SlaveResize (Option *opt)
806 static int slaveW, slaveH, w, h;
809 gtk_widget_get_allocation(shells[DummyDlg], &a);
810 w = a.width; h = a.height;
811 gtk_widget_get_allocation(opt->handle, &a);
812 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
813 slaveH = h - a.height + 13;
815 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
819 LoadIconFile (gchar *svgFilename)
823 snprintf(buf, MSG_SIZ, "%s/%s" IMG, svgDir, svgFilename);
824 return gdk_pixbuf_new_from_file(buf, NULL);
828 static char clickedFile[MSG_SIZ];
832 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
833 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
834 if(suppress) { // we just started XBoard without arguments
835 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
836 } else { // we are running something presumably useful
838 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
839 system(buf); // start new instance on this file
844 GtkosxApplication *theApp;
848 main (int argc, char **argv)
850 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
851 int boardWidth, w, h; //, boardHeight;
853 int forceMono = False;
855 srandom(time(0)); // [HGM] book: make random truly random
857 setbuf(stdout, NULL);
858 setbuf(stderr, NULL);
861 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
862 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
866 if(argc > 1 && !strcmp(argv[1], "--help" )) {
872 gtk_init (&argc, &argv);
874 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
875 char *path = gtkosx_application_get_bundle_path();
876 theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
877 strncpy(dataDir, path, MSG_SIZ);
878 snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
879 snprintf(svgDir, MSG_SIZ, "%s/Contents/Resources/share/xboard/themes/default", path);
880 suppress = (argc == 1 || argc > 1 && argv[1][00] != '-'); // OSX sends signal even if name was already argv[1]!
881 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
882 // we must call application ready before we can get the signal,
883 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
884 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
885 gtkosx_application_ready(theApp);
886 if(argc == 1) { // called without args: OSX open-file signal might follow
887 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
888 usleep(10000); // wait 10 msec (and hope this is long enough).
889 while(gtk_events_pending())
890 gtk_main_iteration(); // process all events that came in upto now
891 suppress = 0; // future open-file signals should start new instance
892 if(clickedFile[0]) { // we were sent an open-file signal with filename!
893 fakeArgv[0] = argv[0];
894 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
900 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
901 typedef struct {char *name, *value; } Config;
902 static Config configList[] = {
903 { "Datadir", DATADIR },
904 { "Sysconfdir", SYSCONFDIR },
909 for(i=0; configList[i].name; i++) {
910 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
911 if(argc > 2) printf("%s", configList[i].value);
912 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
917 /* set up keyboard accelerators group */
918 GtkAccelerators = gtk_accel_group_new();
920 programName = strrchr(argv[0], '/');
921 if (programName == NULL)
922 programName = argv[0];
927 // if (appData.debugMode) {
928 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
931 bindtextdomain(PACKAGE, LOCALEDIR);
932 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
936 appData.boardSize = "";
937 InitAppData(ConvertToLine(argc, argv));
939 if (p == NULL) p = "/tmp";
940 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
941 gameCopyFilename = (char*) malloc(i);
942 gamePasteFilename = (char*) malloc(i);
943 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
944 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
946 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
947 static char buf[MSG_SIZ];
948 snprintf(buf, MSG_SIZ, appData.sysOpen, DATADIR);
949 ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
950 EscapeExpand(buf, appData.firstInitString);
951 appData.firstInitString = strdup(buf);
952 EscapeExpand(buf, appData.secondInitString);
953 appData.secondInitString = strdup(buf);
954 EscapeExpand(buf, appData.firstComputerString);
955 appData.firstComputerString = strdup(buf);
956 EscapeExpand(buf, appData.secondComputerString);
957 appData.secondComputerString = strdup(buf);
960 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
963 if (chdir(chessDir) != 0) {
964 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
970 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
971 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
972 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
973 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
976 setbuf(debugFP, NULL);
980 if (appData.debugMode) {
981 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
985 /* [HGM,HR] make sure board size is acceptable */
986 if(appData.NrFiles > BOARD_FILES ||
987 appData.NrRanks > BOARD_RANKS )
988 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
991 /* This feature does not work; animation needs a rewrite */
992 appData.highlightDragging = FALSE;
996 gameInfo.variant = StringToVariant(appData.variant);
1000 * determine size, based on supplied or remembered -size, or screen size
1002 if (isdigit(appData.boardSize[0])) {
1003 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1004 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1005 &fontPxlSize, &smallLayout, &tinyLayout);
1007 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1008 programName, appData.boardSize);
1012 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // scale height
1014 /* Find some defaults; use the nearest known size */
1015 SizeDefaults *szd, *nearest;
1016 int distance = 99999;
1017 nearest = szd = sizeDefaults;
1018 while (szd->name != NULL) {
1019 if (abs(szd->squareSize - squareSize) < distance) {
1021 distance = abs(szd->squareSize - squareSize);
1022 if (distance == 0) break;
1026 if (i < 2) lineGap = nearest->lineGap;
1027 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1028 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1029 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1030 if (i < 6) smallLayout = nearest->smallLayout;
1031 if (i < 7) tinyLayout = nearest->tinyLayout;
1034 SizeDefaults *szd = sizeDefaults;
1035 if (*appData.boardSize == NULLCHAR) {
1036 // GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow)); // TODO: this does not work, as no mainwindow yet
1037 GdkScreen *screen = gdk_screen_get_default();
1038 guint screenwidth = gdk_screen_get_width(screen);
1039 guint screenheight = gdk_screen_get_height(screen);
1040 while (screenwidth < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1041 screenheight < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1044 if (szd->name == NULL) szd--;
1045 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1047 while (szd->name != NULL &&
1048 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1049 if (szd->name == NULL) {
1050 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1051 programName, appData.boardSize);
1055 squareSize = szd->squareSize;
1056 lineGap = szd->lineGap;
1057 clockFontPxlSize = szd->clockFontPxlSize;
1058 coordFontPxlSize = szd->coordFontPxlSize;
1059 fontPxlSize = szd->fontPxlSize;
1060 smallLayout = szd->smallLayout;
1061 tinyLayout = szd->tinyLayout;
1062 // [HGM] font: use defaults from settings file if available and not overruled
1065 defaultLineGap = lineGap;
1066 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1068 /* [HR] height treated separately (hacked) */
1069 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1070 // boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1073 * Determine what fonts to use.
1075 InitializeFonts((2*clockFontPxlSize+1)/3, coordFontPxlSize, fontPxlSize);
1078 * Detect if there are not enough colors available and adapt.
1081 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1082 appData.monoMode = True;
1086 forceMono = MakeColors();
1089 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1091 appData.monoMode = True;
1094 ParseIcsTextColors();
1100 layoutName = "tinyLayout";
1101 } else if (smallLayout) {
1102 layoutName = "smallLayout";
1104 layoutName = "normalLayout";
1107 wpMain.width = -1; // prevent popup sizes window
1108 optList = BoardPopUp(squareSize, lineGap, (void*)
1118 InitDrawingHandle(optList + W_BOARD);
1119 shellWidget = shells[BoardWindow];
1120 currBoard = &optList[W_BOARD];
1121 boardWidget = optList[W_BOARD].handle;
1122 menuBarWidget = optList[W_MENU].handle;
1123 dropMenu = optList[W_DROP].handle;
1124 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1126 formWidget = XtParent(boardWidget);
1127 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1128 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1129 XtGetValues(optList[W_WHITE].handle, args, 2);
1130 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1131 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1132 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1133 XtGetValues(optList[W_PAUSE].handle, args, 2);
1137 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1138 // not need to go into InitDrawingSizes().
1142 // add accelerators to main shell
1143 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1146 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1148 WhiteIcon = LoadIconFile("icon_white");
1149 BlackIcon = LoadIconFile("icon_black");
1150 SetClockIcon(0); // sets white icon
1154 * Create a cursor for the board widget.
1157 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1158 XChangeWindowAttributes(xDisplay, xBoardWindow,
1159 CWCursor, &window_attributes);
1163 * Inhibit shell resizing.
1166 shellArgs[0].value = (XtArgVal) &w;
1167 shellArgs[1].value = (XtArgVal) &h;
1168 XtGetValues(shellWidget, shellArgs, 2);
1169 shellArgs[4].value = shellArgs[2].value = w;
1170 shellArgs[5].value = shellArgs[3].value = h;
1171 // XtSetValues(shellWidget, &shellArgs[2], 4);
1174 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1175 // It wil only become known asynchronously, when we first write a string into it.
1176 // This will then change the clock widget height, which triggers resizing the top-level window
1177 // and a configure event. Only then can we know the total height of the top-level window,
1178 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1179 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1182 gtk_widget_get_allocation(shells[BoardWindow], &a);
1183 w = a.width; h = a.height;
1184 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1185 clockKludge = hc = a.height;
1186 gtk_widget_get_allocation(boardWidget, &a);
1187 // marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1188 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1194 if(appData.logoSize)
1195 { // locate and read user logo
1197 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1198 ASSIGN(userLogo, buf);
1201 if (appData.animate || appData.animateDragging)
1204 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1205 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1207 /* [AS] Restore layout */
1208 if( wpMoveHistory.visible ) {
1212 if( wpEvalGraph.visible )
1217 if( wpEngineOutput.visible ) {
1218 EngineOutputPopUp();
1221 if( wpConsole.visible && appData.icsActive ) {
1226 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1231 if (errorExitStatus == -1) {
1232 if (appData.icsActive) {
1233 /* We now wait until we see "login:" from the ICS before
1234 sending the logon script (problems with timestamp otherwise) */
1235 /*ICSInitScript();*/
1236 if (appData.icsInputBox) ICSInputBoxPopUp();
1240 signal(SIGWINCH, TermSizeSigHandler);
1242 signal(SIGINT, IntSigHandler);
1243 signal(SIGTERM, IntSigHandler);
1244 if (*appData.cmailGameName != NULLCHAR) {
1245 signal(SIGUSR1, CmailSigHandler);
1250 // XtSetKeyboardFocus(shellWidget, formWidget);
1252 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1255 /* check for GTK events and process them */
1258 gtk_main_iteration();
1261 if (appData.debugMode) fclose(debugFP); // [DM] debug
1268 while(gtk_events_pending()) gtk_main_iteration();
1272 TermSizeSigHandler (int sig)
1278 IntSigHandler (int sig)
1284 CmailSigHandler (int sig)
1289 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1291 /* Activate call-back function CmailSigHandlerCallBack() */
1292 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1294 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1298 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1301 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1303 /**** end signal code ****/
1306 #define Abs(n) ((n)<0 ? -(n) : (n))
1309 InsertPxlSize (char *pattern, int targetPxlSize)
1312 snprintf(buf, MSG_SIZ, pattern, targetPxlSize); // pattern is something like "Sans Bold %d"
1319 InsertPxlSize (char *pattern, int targetPxlSize)
1321 char *base_fnt_lst, strInt[12], *p, *q;
1322 int alternatives, i, len, strIntLen;
1325 * Replace the "*" (if present) in the pixel-size slot of each
1326 * alternative with the targetPxlSize.
1330 while ((p = strchr(p, ',')) != NULL) {
1334 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1335 strIntLen = strlen(strInt);
1336 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1340 while (alternatives--) {
1341 char *comma = strchr(p, ',');
1342 for (i=0; i<14; i++) {
1343 char *hyphen = strchr(p, '-');
1345 if (comma && hyphen > comma) break;
1346 len = hyphen + 1 - p;
1347 if (i == 7 && *p == '*' && len == 2) {
1349 memcpy(q, strInt, strIntLen);
1359 len = comma + 1 - p;
1366 return base_fnt_lst;
1372 CreateFontSet (char *base_fnt_lst)
1375 char **missing_list;
1379 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1380 &missing_list, &missing_count, &def_string);
1381 if (appData.debugMode) {
1383 XFontStruct **font_struct_list;
1384 char **font_name_list;
1385 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1387 fprintf(debugFP, " got list %s, locale %s\n",
1388 XBaseFontNameListOfFontSet(fntSet),
1389 XLocaleOfFontSet(fntSet));
1390 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1391 for (i = 0; i < count; i++) {
1392 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1395 for (i = 0; i < missing_count; i++) {
1396 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1399 if (fntSet == NULL) {
1400 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1406 #else // not ENABLE_NLS
1408 * Find a font that matches "pattern" that is as close as
1409 * possible to the targetPxlSize. Prefer fonts that are k
1410 * pixels smaller to fonts that are k pixels larger. The
1411 * pattern must be in the X Consortium standard format,
1412 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1413 * The return value should be freed with XtFree when no
1418 FindFont (char *pattern, int targetPxlSize)
1420 char **fonts, *p, *best, *scalable, *scalableTail;
1421 int i, j, nfonts, minerr, err, pxlSize;
1423 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1425 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1426 programName, pattern);
1433 for (i=0; i<nfonts; i++) {
1436 if (*p != '-') continue;
1438 if (*p == NULLCHAR) break;
1439 if (*p++ == '-') j++;
1441 if (j < 7) continue;
1444 scalable = fonts[i];
1447 err = pxlSize - targetPxlSize;
1448 if (Abs(err) < Abs(minerr) ||
1449 (minerr > 0 && err < 0 && -err == minerr)) {
1455 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1456 /* If the error is too big and there is a scalable font,
1457 use the scalable font. */
1458 int headlen = scalableTail - scalable;
1459 p = (char *) XtMalloc(strlen(scalable) + 10);
1460 while (isdigit(*scalableTail)) scalableTail++;
1461 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1463 p = (char *) XtMalloc(strlen(best) + 2);
1464 safeStrCpy(p, best, strlen(best)+1 );
1466 if (appData.debugMode) {
1467 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1468 pattern, targetPxlSize, p);
1470 XFreeFontNames(fonts);
1477 MarkMenuItem (char *menuRef, int state)
1479 MenuItem *item = MenuNameToItem(menuRef);
1481 if(item && item->handle) {
1482 ((GtkCheckMenuItem *) (item->handle))->active = state;
1488 EnableNamedMenuItem (char *menuRef, int state)
1490 MenuItem *item = MenuNameToItem(menuRef);
1492 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1497 EnableButtonBar (int state)
1500 XtSetSensitive(optList[W_BUTTON].handle, state);
1506 SetMenuEnables (Enables *enab)
1508 while (enab->name != NULL) {
1509 EnableNamedMenuItem(enab->name, enab->value);
1514 gboolean KeyPressProc(window, eventkey, data)
1516 GdkEventKey *eventkey;
1520 MoveTypeInProc(eventkey); // pop up for typed in moves
1523 /* check for other key values */
1524 switch(eventkey->keyval) {
1536 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1537 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1539 if(*nprms == 0) return;
1540 item = MenuNameToItem(prms[0]);
1541 if(item) ((MenuProc *) item->proc) ();
1555 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1556 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1557 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1558 dmEnables[i].piece);
1559 XtSetSensitive(entry, p != NULL || !appData.testLegality
1560 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1561 && !appData.icsActive));
1563 while (p && *p++ == dmEnables[i].piece) count++;
1564 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1566 XtSetArg(args[j], XtNlabel, label); j++;
1567 XtSetValues(entry, args, j);
1573 do_flash_delay (unsigned long msec)
1579 FlashDelay (int flash_delay)
1581 if(flash_delay) do_flash_delay(flash_delay);
1585 Fraction (int x, int start, int stop)
1587 double f = ((double) x - start)/(stop - start);
1588 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1592 static WindowPlacement wpNew;
1595 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1597 int touch=0, fudge = 4, f = 3;
1598 GetActualPlacement(sh, wp);
1599 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1600 if(abs(wp->x + wp->width + 2*frameX - f - wpMain.x) < fudge) touch = 2; else // left touch
1601 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1602 if(abs(wp->y + wp->height + frameX + frameY - f - wpMain.y) < fudge) touch = 4; // top touch
1603 //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);
1604 if(!touch ) return; // only windows that touch co-move
1605 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1606 int heightInc = wpNew.height - wpMain.height;
1607 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1608 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1609 wp->y += fracTop * heightInc;
1610 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1612 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1614 wp->height += heightInc;
1615 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1616 int widthInc = wpNew.width - wpMain.width;
1617 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1618 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1619 wp->y += fracLeft * widthInc;
1620 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1622 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1624 wp->width += widthInc;
1626 wp->x += wpNew.x - wpMain.x;
1627 wp->y += wpNew.y - wpMain.y;
1628 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1629 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1631 XtSetArg(args[j], XtNx, wp->x); j++;
1632 XtSetArg(args[j], XtNy, wp->y); j++;
1633 XtSetValues(sh, args, j);
1635 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1636 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1637 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1641 ReSize (WindowPlacement *wp)
1644 int sqx, sqy, w, h, hc, lg = lineGap;
1645 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1646 hc = a.height; // clock height can depend on single / double line clock text!
1647 if(clockKludge && hc != clockKludge) wp->height += hc - clockKludge, clockKludge = 0;
1648 wpMain.height = BOARD_HEIGHT * (squareSize + lineGap) + lineGap + marginH + hc;
1649 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1650 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1651 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1652 if(sqy < sqx) sqx = sqy;
1653 if(sqx < 20) return;
1654 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1656 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1657 sqx = (wp->width - lg - marginW) / BOARD_WIDTH - lg;
1658 sqy = (wp->height - lg - marginH - hc) / BOARD_HEIGHT - lg;
1659 if(sqy < sqx) sqx = sqy;
1660 lg = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1661 if(sqx == oldSqx + 1 && lg == lineGap + 1) sqx = oldSqx, squareSize = 0; // prevent oscillations, force resize by kludge
1663 if(sqx != squareSize) {
1664 squareSize = sqx; // adopt new square size
1665 CreatePNGPieces(); // make newly scaled pieces
1666 InitDrawingSizes(0, 0); // creates grid etc.
1667 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1668 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1669 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1670 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1671 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1674 static guint delayedDragTag = 0;
1683 // GetActualPlacement(shellWidget, &wpNew);
1684 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1685 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1686 busy = 0; return; // false alarm
1689 if(appData.useStickyWindows) {
1690 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1691 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1692 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1693 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1694 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1697 DrawPosition(True, NULL);
1698 if(delayedDragTag) g_source_remove(delayedDragTag);
1699 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1706 //printf("old timr = %d\n", delayedDragTag);
1707 if(delayedDragTag) g_source_remove(delayedDragTag);
1708 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1709 //printf("new timr = %d\n", delayedDragTag);
1713 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1715 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1717 wpNew.x = event->configure.x;
1718 wpNew.y = event->configure.y;
1719 wpNew.width = event->configure.width;
1720 wpNew.height = event->configure.height;
1721 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1727 /* Disable all user input other than deleting the window */
1728 static int frozen = 0;
1734 /* Grab by a widget that doesn't accept input */
1735 gtk_grab_add(optList[W_MESSG].handle);
1739 /* Undo a FreezeUI */
1743 if (!frozen) return;
1744 gtk_grab_remove(optList[W_MESSG].handle);
1751 static int oldPausing = FALSE;
1752 static GameMode oldmode = (GameMode) -1;
1754 if (!boardWidget) return;
1756 if (pausing != oldPausing) {
1757 oldPausing = pausing;
1758 MarkMenuItem("Mode.Pause", pausing);
1760 if (appData.showButtonBar) {
1761 /* Always toggle, don't set. Previous code messes up when
1762 invoked while the button is pressed, as releasing it
1763 toggles the state again. */
1765 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1766 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1770 wname = ModeToWidgetName(oldmode);
1771 if (wname != NULL) {
1772 MarkMenuItem(wname, False);
1774 wname = ModeToWidgetName(gameMode);
1775 if (wname != NULL) {
1776 MarkMenuItem(wname, True);
1779 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1781 /* Maybe all the enables should be handled here, not just this one */
1782 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1784 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1789 * Button/menu procedures
1792 void CopyFileToClipboard(gchar *filename)
1794 gchar *selection_tmp;
1798 FILE* f = fopen(filename, "r");
1801 if (f == NULL) return;
1805 selection_tmp = g_try_malloc(len + 1);
1806 if (selection_tmp == NULL) {
1807 printf("Malloc failed in CopyFileToClipboard\n");
1810 count = fread(selection_tmp, 1, len, f);
1813 g_free(selection_tmp);
1816 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1818 // copy selection_tmp to clipboard
1819 GdkDisplay *gdisp = gdk_display_get_default();
1821 g_free(selection_tmp);
1824 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1825 gtk_clipboard_set_text(cb, selection_tmp, -1);
1826 g_free(selection_tmp);
1830 CopySomething (char *src)
1832 GdkDisplay *gdisp = gdk_display_get_default();
1834 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1835 if (gdisp == NULL) return;
1836 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1837 gtk_clipboard_set_text(cb, src, -1);
1841 PastePositionProc ()
1843 GdkDisplay *gdisp = gdk_display_get_default();
1847 if (gdisp == NULL) return;
1848 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1849 fenstr = gtk_clipboard_wait_for_text(cb);
1850 if (fenstr==NULL) return; // nothing had been selected to copy
1851 EditPositionPasteFEN(fenstr);
1863 // get game from clipboard
1864 GdkDisplay *gdisp = gdk_display_get_default();
1865 if (gdisp == NULL) return;
1866 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1867 text = gtk_clipboard_wait_for_text(cb);
1868 if (text == NULL) return; // nothing to paste
1871 // write to temp file
1872 if (text == NULL || len == 0) {
1873 return; //nothing to paste
1875 f = fopen(gamePasteFilename, "w");
1877 DisplayError(_("Can't open temp file"), errno);
1880 fwrite(text, 1, len, f);
1884 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1891 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1897 void MoveTypeInProc(eventkey)
1898 GdkEventKey *eventkey;
1902 // ingnore if ctrl, alt, or meta is pressed
1903 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1907 buf[0]=eventkey->keyval;
1909 if (eventkey->keyval > 32 && eventkey->keyval < 256)
1910 ConsoleAutoPopUp (buf);
1915 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1917 if (!TempBackwardActive) {
1918 TempBackwardActive = True;
1924 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1926 /* Check to see if triggered by a key release event for a repeating key.
1927 * If so the next queued event will be a key press of the same key at the same time */
1928 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1930 XPeekEvent(xDisplay, &next);
1931 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1932 next.xkey.keycode == event->xkey.keycode)
1936 TempBackwardActive = False;
1942 { // called from menu
1945 snprintf(buf, MSG_SIZ, "%s ./man.command", appData.sysOpen);
1948 system("xterm -e man xboard &");
1953 SetWindowTitle (char *text, char *title, char *icon)
1958 if (appData.titleInWindow) {
1960 XtSetArg(args[i], XtNlabel, text); i++;
1961 XtSetValues(titleWidget, args, i);
1964 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
1965 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
1966 XtSetValues(shellWidget, args, i);
1967 XSync(xDisplay, False);
1969 if (appData.titleInWindow) {
1970 SetWidgetLabel(titleWidget, text);
1972 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1977 DisplayIcsInteractionTitle (String message)
1980 if (oldICSInteractionTitle == NULL) {
1981 /* Magic to find the old window title, adapted from vim */
1982 char *wina = getenv("WINDOWID");
1984 Window win = (Window) atoi(wina);
1985 Window root, parent, *children;
1986 unsigned int nchildren;
1987 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1989 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1990 if (!XQueryTree(xDisplay, win, &root, &parent,
1991 &children, &nchildren)) break;
1992 if (children) XFree((void *)children);
1993 if (parent == root || parent == 0) break;
1996 XSetErrorHandler(oldHandler);
1998 if (oldICSInteractionTitle == NULL) {
1999 oldICSInteractionTitle = "xterm";
2002 printf("\033]0;%s\007", message);
2009 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2011 GtkWidget *w = (GtkWidget *) opt->handle;
2018 strcpy(bgcolor, "black");
2019 strcpy(fgcolor, "white");
2021 strcpy(bgcolor, "white");
2022 strcpy(fgcolor, "black");
2025 appData.lowTimeWarning &&
2026 (timer / 1000) < appData.icsAlarmTime) {
2027 strcpy(fgcolor, appData.lowTimeWarningColor);
2030 gdk_color_parse( bgcolor, &col );
2031 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2033 if (appData.clockMode) {
2034 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2035 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2036 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2037 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2039 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2040 bgcolor, fgcolor, color);
2041 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2042 // bgcolor, fgcolor, color);
2044 gtk_label_set_markup(GTK_LABEL(w), markup);
2048 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2051 SetClockIcon (int color)
2053 GdkPixbuf *pm = *clockIcons[color];
2054 if (mainwindowIcon != pm) {
2055 mainwindowIcon = pm;
2057 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2059 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2064 #define INPUT_SOURCE_BUF_SIZE 8192
2073 char buf[INPUT_SOURCE_BUF_SIZE];
2078 DoInputCallback(io, cond, data)
2083 /* read input from one of the input source (for example a chess program, ICS, etc).
2084 * and call a function that will handle the input
2091 /* All information (callback function, file descriptor, etc) is
2092 * saved in an InputSource structure
2094 InputSource *is = (InputSource *) data;
2096 if (is->lineByLine) {
2097 count = read(is->fd, is->unused,
2098 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2100 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2101 RemoveInputSource(is); // cease reading stdin
2102 stdoutClosed = TRUE; // suppress future output
2105 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2108 is->unused += count;
2110 /* break input into lines and call the callback function on each
2113 while (p < is->unused) {
2114 q = memchr(p, '\n', is->unused - p);
2115 if (q == NULL) break;
2117 (is->func)(is, is->closure, p, q - p, 0);
2120 /* remember not yet used part of the buffer */
2122 while (p < is->unused) {
2127 /* read maximum length of input buffer and send the whole buffer
2128 * to the callback function
2130 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2135 (is->func)(is, is->closure, is->buf, count, error);
2137 return True; // Must return true or the watch will be removed
2140 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2147 GIOChannel *channel;
2148 ChildProc *cp = (ChildProc *) pr;
2150 is = (InputSource *) calloc(1, sizeof(InputSource));
2151 is->lineByLine = lineByLine;
2155 is->fd = fileno(stdin);
2157 is->kind = cp->kind;
2158 is->fd = cp->fdFrom;
2161 is->unused = is->buf;
2165 /* GTK-TODO: will this work on windows?*/
2167 channel = g_io_channel_unix_new(is->fd);
2168 g_io_channel_set_close_on_unref (channel, TRUE);
2169 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2171 is->closure = closure;
2172 return (InputSourceRef) is;
2177 RemoveInputSource(isr)
2180 InputSource *is = (InputSource *) isr;
2182 if (is->sid == 0) return;
2183 g_source_remove(is->sid);
2190 static Boolean frameWaiting;
2193 FrameAlarm (int sig)
2195 frameWaiting = False;
2196 /* In case System-V style signals. Needed?? */
2197 signal(SIGALRM, FrameAlarm);
2201 FrameDelay (int time)
2203 struct itimerval delay;
2206 frameWaiting = True;
2207 signal(SIGALRM, FrameAlarm);
2208 delay.it_interval.tv_sec =
2209 delay.it_value.tv_sec = time / 1000;
2210 delay.it_interval.tv_usec =
2211 delay.it_value.tv_usec = (time % 1000) * 1000;
2212 setitimer(ITIMER_REAL, &delay, NULL);
2213 while (frameWaiting) pause();
2214 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2215 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2216 setitimer(ITIMER_REAL, &delay, NULL);
2223 FrameDelay (int time)
2226 XSync(xDisplay, False);
2228 // gtk_main_iteration_do(False);
2231 usleep(time * 1000);
2237 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2239 char buf[MSG_SIZ], *logoName = buf;
2241 if(appData.logo[n][0]) {
2242 logoName = appData.logo[n];
2243 } else if(appData.autoLogo) {
2244 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2245 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2246 } else { // engine; look in engine-dir (if any) first
2247 snprintf(buf, MSG_SIZ, "%s/logo.png", appData.directory[n]);
2248 if(appData.directory[n] && appData.directory[n][0]
2249 && strcmp(appData.directory[n], ".") && (f = fopen(buf, "r")) )
2251 else // no engine dir or no logo.png in it: look in logo dir
2252 if(appData.logoDir && appData.logoDir[0])
2253 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2257 { ASSIGN(cps->programLogo, logoName); }
2261 UpdateLogos (int displ)
2263 if(optList[W_WHITE-1].handle == NULL) return;
2264 LoadLogo(&first, 0, 0);
2265 LoadLogo(&second, 1, appData.icsActive);
2266 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2270 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2281 GtkFileFilter *gtkfilter;
2282 GtkFileFilter *gtkfilter_all;
2284 char fileext[10] = "";
2285 char *result = NULL;
2288 /* make a copy of the filter string, so that strtok can work with it*/
2289 cp = strdup(filter);
2291 /* add filters for file extensions */
2292 gtkfilter = gtk_file_filter_new();
2293 gtkfilter_all = gtk_file_filter_new();
2295 /* one filter to show everything */
2296 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2297 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2299 /* add filter if present */
2300 result = strtok(cp, space);
2301 while( result != NULL ) {
2302 snprintf(fileext,10,"*%s",result);
2303 result = strtok( NULL, space );
2304 gtk_file_filter_add_pattern(gtkfilter, fileext);
2307 /* second filter to only show what's useful */
2308 gtk_file_filter_set_name (gtkfilter,filter);
2310 if (openMode[0] == 'r')
2312 dialog = gtk_file_chooser_dialog_new (label,
2314 GTK_FILE_CHOOSER_ACTION_OPEN,
2315 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2316 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2321 dialog = gtk_file_chooser_dialog_new (label,
2323 GTK_FILE_CHOOSER_ACTION_SAVE,
2324 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2325 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2327 /* add filename suggestions */
2328 if (strlen(def) > 0 )
2329 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2331 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2335 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2336 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2337 /* activate filter */
2338 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2340 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2345 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2348 f = fopen(filename, openMode);
2351 DisplayError(_("Failed to open file"), errno);
2355 /* TODO add indec */
2357 ASSIGN(*name, filename);
2358 ScheduleDelayedEvent(DelayedLoad, 50);
2363 gtk_widget_destroy (dialog);