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"
162 #include "xgamelist.h"
163 #include "xhistory.h"
167 #include "engineoutput.h"
177 #define usleep(t) _sleep2(((t)+500)/1000)
181 # define _(s) gettext (s)
182 # define N_(s) gettext_noop (s)
188 int main P((int argc, char **argv));
189 RETSIGTYPE CmailSigHandler P((int sig));
190 RETSIGTYPE IntSigHandler P((int sig));
191 RETSIGTYPE TermSizeSigHandler P((int sig));
193 char *InsertPxlSize P((char *pattern, int targetPxlSize));
194 XFontSet CreateFontSet P((char *base_fnt_lst));
196 char *FindFont P((char *pattern, int targetPxlSize));
198 void DelayedDrag P((void));
199 void ICSInputBoxPopUp P((void));
200 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
202 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
203 void HandlePV P((Widget w, XEvent * event,
204 String * params, Cardinal * nParams));
205 void DrawPositionProc P((Widget w, XEvent *event,
206 String *prms, Cardinal *nprms));
207 void CommentClick P((Widget w, XEvent * event,
208 String * params, Cardinal * nParams));
209 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
210 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
211 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
212 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
213 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
214 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
215 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
216 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
217 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
218 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
220 Boolean TempBackwardActive = False;
221 void DisplayMove P((int moveNumber));
222 void ICSInitScript P((void));
223 void update_ics_width P(());
224 int CopyMemoProc P(());
225 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
229 * XBoard depends on Xt R4 or higher
231 int xtVersion = XtSpecificationRelease;
236 Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
237 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
238 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
240 XFontSet fontSet, clockFontSet;
243 XFontStruct *clockFontStruct;
245 Font coordFontID, countFontID;
246 XFontStruct *coordFontStruct, *countFontStruct;
247 XtAppContext appContext;
249 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
251 GtkWidget *mainwindow;
253 Option *optList; // contains all widgets of main window
256 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
259 static GdkPixbuf *mainwindowIcon=NULL;
260 static GdkPixbuf *WhiteIcon=NULL;
261 static GdkPixbuf *BlackIcon=NULL;
263 typedef unsigned int BoardSize;
265 Boolean chessProgram;
267 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
268 int smallLayout = 0, tinyLayout = 0,
269 marginW, marginH, // [HGM] for run-time resizing
270 fromX = -1, fromY = -1, toX, toY, commentUp = False,
271 errorExitStatus = -1, defaultLineGap;
273 Dimension textHeight;
274 Pixel timerForegroundPixel, timerBackgroundPixel;
275 Pixel buttonForegroundPixel, buttonBackgroundPixel;
277 char *chessDir, *programName, *programVersion;
278 Boolean alwaysOnTop = False;
279 char *icsTextMenuString;
281 char *firstChessProgramNames;
282 char *secondChessProgramNames;
284 WindowPlacement wpMain;
285 WindowPlacement wpConsole;
286 WindowPlacement wpComment;
287 WindowPlacement wpMoveHistory;
288 WindowPlacement wpEvalGraph;
289 WindowPlacement wpEngineOutput;
290 WindowPlacement wpGameList;
291 WindowPlacement wpTags;
293 /* This magic number is the number of intermediate frames used
294 in each half of the animation. For short moves it's reduced
295 by 1. The total number of frames will be factor * 2 + 1. */
298 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
305 DropMenuEnables dmEnables[] = {
323 XtResource clientResources[] = {
324 { "flashCount", "flashCount", XtRInt, sizeof(int),
325 XtOffset(AppDataPtr, flashCount), XtRImmediate,
326 (XtPointer) FLASH_COUNT },
329 XrmOptionDescRec shellOptions[] = {
330 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
331 { "-flash", "flashCount", XrmoptionNoArg, "3" },
332 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
335 XtActionsRec boardActions[] = {
336 { "DrawPosition", DrawPositionProc },
337 { "HandlePV", HandlePV },
338 { "SelectPV", SelectPV },
339 { "StopPV", StopPV },
340 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
341 { "QuitProc", QuitWrapper },
342 { "ManProc", ManInner },
343 { "TempBackwardProc", TempBackwardProc },
344 { "TempForwardProc", TempForwardProc },
345 { "CommentClick", (XtActionProc) CommentClick },
346 { "GenericPopDown", (XtActionProc) GenericPopDown },
347 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
348 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
349 { "SelectMove", (XtActionProc) SelectMove },
350 { "LoadSelectedProc", LoadSelectedProc },
351 { "SetFilterProc", SetFilterProc },
352 { "TypeInProc", TypeInProc },
353 { "EnterKeyProc", EnterKeyProc },
354 { "UpKeyProc", UpKeyProc },
355 { "DownKeyProc", DownKeyProc },
356 { "WheelProc", WheelProc },
357 { "TabProc", TabProc },
361 char globalTranslations[] =
362 ":<Key>F9: MenuItem(Actions.Resign) \n \
363 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
364 :Meta<Key>V: MenuItem(File.NewVariant) \n \
365 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
366 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
367 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
368 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
369 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
370 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
371 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
372 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
373 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
374 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
375 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
376 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
377 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
378 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
379 :Ctrl<Key>q: MenuItem(File.Quit) \n \
380 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
381 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
382 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
383 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
384 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
385 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
386 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
387 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
388 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
389 :Meta<Key>G: MenuItem(View.GameList) \n \
390 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
391 :<Key>Pause: MenuItem(Mode.Pause) \n \
392 :<Key>F3: MenuItem(Action.Accept) \n \
393 :<Key>F4: MenuItem(Action.Decline) \n \
394 :<Key>F12: MenuItem(Action.Rematch) \n \
395 :<Key>F5: MenuItem(Action.CallFlag) \n \
396 :<Key>F6: MenuItem(Action.Draw) \n \
397 :<Key>F7: MenuItem(Action.Adjourn) \n \
398 :<Key>F8: MenuItem(Action.Abort) \n \
399 :<Key>F10: MenuItem(Action.StopObserving) \n \
400 :<Key>F11: MenuItem(Action.StopExamining) \n \
401 :Ctrl<Key>d: MenuItem(DebugProc) \n \
402 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
403 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
404 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
405 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
406 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
407 :<Key>Left: MenuItem(Edit.Backward) \n \
408 :<Key>Right: MenuItem(Edit.Forward) \n \
409 :<Key>Home: MenuItem(Edit.Revert) \n \
410 :<Key>End: MenuItem(Edit.TruncateGame) \n \
411 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
412 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
413 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
414 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
415 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
416 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
417 #ifndef OPTIONSDIALOG
419 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
420 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
421 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
422 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
423 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
426 :<Key>F1: MenuItem(Help.ManXBoard) \n \
427 :<Key>F2: MenuItem(View.FlipView) \n \
428 :<KeyDown>Return: TempBackwardProc() \n \
429 :<KeyUp>Return: TempForwardProc() \n";
431 char ICSInputTranslations[] =
432 "<Key>Up: UpKeyProc() \n "
433 "<Key>Down: DownKeyProc() \n "
434 "<Key>Return: EnterKeyProc() \n";
436 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
437 // as the widget is destroyed before the up-click can call extend-end
438 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
441 String xboardResources[] = {
442 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
447 /* Max possible square size */
448 #define MAXSQSIZE 256
450 static int xpm_avail[MAXSQSIZE];
452 #ifdef HAVE_DIR_STRUCT
454 /* Extract piece size from filename */
456 xpm_getsize (char *name, int len, char *ext)
464 if ((p=strchr(name, '.')) == NULL ||
465 StrCaseCmp(p+1, ext) != 0)
471 while (*p && isdigit(*p))
478 /* Setup xpm_avail */
480 xpm_getavail (char *dirname, char *ext)
486 for (i=0; i<MAXSQSIZE; ++i)
489 if (appData.debugMode)
490 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
492 dir = opendir(dirname);
495 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
496 programName, dirname);
500 while ((ent=readdir(dir)) != NULL) {
501 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
502 if (i > 0 && i < MAXSQSIZE)
512 xpm_print_avail (FILE *fp, char *ext)
516 fprintf(fp, _("Available `%s' sizes:\n"), ext);
517 for (i=1; i<MAXSQSIZE; ++i) {
523 /* Return XPM piecesize closest to size */
525 xpm_closest_to (char *dirname, int size, char *ext)
528 int sm_diff = MAXSQSIZE;
532 xpm_getavail(dirname, ext);
534 if (appData.debugMode)
535 xpm_print_avail(stderr, ext);
537 for (i=1; i<MAXSQSIZE; ++i) {
540 diff = (diff<0) ? -diff : diff;
541 if (diff < sm_diff) {
549 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
555 #else /* !HAVE_DIR_STRUCT */
556 /* If we are on a system without a DIR struct, we can't
557 read the directory, so we can't collect a list of
558 filenames, etc., so we can't do any size-fitting. */
560 xpm_closest_to (char *dirname, int size, char *ext)
563 Warning: No DIR structure found on this system --\n\
564 Unable to autosize for XPM/XIM pieces.\n\
565 Please report this error to %s.\n\
566 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
569 #endif /* HAVE_DIR_STRUCT */
573 /* Arrange to catch delete-window events */
574 Atom wm_delete_window;
576 CatchDeleteWindow (Widget w, String procname)
579 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
580 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
581 XtAugmentTranslations(w, XtParseTranslationTable(buf));
588 gtk_window_present(GTK_WINDOW(mainwindow));
591 //---------------------------------------------------------------------------------------------------------
592 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
595 #define CW_USEDEFAULT (1<<31)
596 #define ICS_TEXT_MENU_SIZE 90
597 #define DEBUG_FILE "xboard.debug"
598 #define SetCurrentDirectory chdir
599 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
603 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
606 // front-end part of option handling
608 // [HGM] This platform-dependent table provides the location for storing the color info
609 extern char *crWhite, * crBlack;
613 &appData.whitePieceColor,
614 &appData.blackPieceColor,
615 &appData.lightSquareColor,
616 &appData.darkSquareColor,
617 &appData.highlightSquareColor,
618 &appData.premoveHighlightColor,
619 &appData.lowTimeWarningColor,
630 // [HGM] font: keep a font for each square size, even non-stndard ones
633 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
634 char *fontTable[NUM_FONTS][MAX_SIZE];
637 ParseFont (char *name, int number)
638 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
640 if(sscanf(name, "size%d:", &size)) {
641 // [HGM] font: font is meant for specific boardSize (likely from settings file);
642 // defer processing it until we know if it matches our board size
643 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
644 fontTable[number][size] = strdup(strchr(name, ':')+1);
645 fontValid[number][size] = True;
650 case 0: // CLOCK_FONT
651 appData.clockFont = strdup(name);
653 case 1: // MESSAGE_FONT
654 appData.font = strdup(name);
656 case 2: // COORD_FONT
657 appData.coordFont = strdup(name);
662 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
667 { // only 2 fonts currently
668 appData.clockFont = CLOCK_FONT_NAME;
669 appData.coordFont = COORD_FONT_NAME;
670 appData.font = DEFAULT_FONT_NAME;
675 { // no-op, until we identify the code for this already in XBoard and move it here
679 ParseColor (int n, char *name)
680 { // in XBoard, just copy the color-name string
681 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
685 ParseTextAttribs (ColorClass cc, char *s)
687 (&appData.colorShout)[cc] = strdup(s);
691 ParseBoardSize (void *addr, char *name)
693 appData.boardSize = strdup(name);
698 { // In XBoard the sound-playing program takes care of obtaining the actual sound
702 SetCommPortDefaults ()
703 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
706 // [HGM] args: these three cases taken out to stay in front-end
708 SaveFontArg (FILE *f, ArgDescriptor *ad)
711 int i, n = (int)(intptr_t)ad->argLoc;
713 case 0: // CLOCK_FONT
714 name = appData.clockFont;
716 case 1: // MESSAGE_FONT
719 case 2: // COORD_FONT
720 name = appData.coordFont;
725 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
726 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
727 fontTable[n][squareSize] = strdup(name);
728 fontValid[n][squareSize] = True;
731 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
732 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
737 { // nothing to do, as the sounds are at all times represented by their text-string names already
741 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
742 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
743 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
747 SaveColor (FILE *f, ArgDescriptor *ad)
748 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
749 if(colorVariable[(int)(intptr_t)ad->argLoc])
750 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
754 SaveBoardSize (FILE *f, char *name, void *addr)
755 { // wrapper to shield back-end from BoardSize & sizeInfo
756 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
760 ParseCommPortSettings (char *s)
761 { // no such option in XBoard (yet)
767 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
771 gtk_widget_get_allocation(shell, &a);
775 wp->height = a.height;
776 printf("placement\n");
777 frameX = a.x; frameY = a.y; // remember to decide if windows touch
781 GetActualPlacement (Widget wg, WindowPlacement *wp)
783 XWindowAttributes winAt;
790 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
791 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
792 wp->x = rx - winAt.x;
793 wp->y = ry - winAt.y;
794 wp->height = winAt.height;
795 wp->width = winAt.width;
796 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
802 { // wrapper to shield use of window handles from back-end (make addressible by number?)
803 // In XBoard this will have to wait until awareness of window parameters is implemented
804 GetActualPlacement(shellWidget, &wpMain);
805 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
806 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
807 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
808 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
809 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
810 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
814 PrintCommPortSettings (FILE *f, char *name)
815 { // This option does not exist in XBoard
819 EnsureOnScreen (int *x, int *y, int minX, int minY)
826 { // [HGM] args: allows testing if main window is realized from back-end
828 return xBoardWindow != 0;
830 return DialogExists(BoardWindow);
835 PopUpStartupDialog ()
836 { // start menu not implemented in XBoard
840 ConvertToLine (int argc, char **argv)
842 static char line[128*1024], buf[1024];
846 for(i=1; i<argc; i++)
848 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
849 && argv[i][0] != '{' )
850 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
852 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
853 strncat(line, buf, 128*1024 - strlen(line) - 1 );
856 line[strlen(line)-1] = NULLCHAR;
860 //--------------------------------------------------------------------------------------------
863 ResizeBoardWindow (int w, int h, int inhibit)
865 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
867 // gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
869 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
871 shellArgs[0].value = w;
872 shellArgs[1].value = h;
873 shellArgs[4].value = shellArgs[2].value = w;
874 shellArgs[5].value = shellArgs[3].value = h;
875 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
877 XSync(xDisplay, False);
883 MakeOneColor (char *name, Pixel *color)
886 if (!appData.monoMode) {
887 vFrom.addr = (caddr_t) name;
888 vFrom.size = strlen(name);
889 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
890 if (vTo.addr == NULL) {
891 appData.monoMode = True;
894 *color = *(Pixel *) vTo.addr;
903 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
904 int forceMono = False;
907 if (appData.lowTimeWarning)
908 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
909 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
910 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
917 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
918 { // detervtomine what fonts to use, and create them
923 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
924 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
925 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
926 appData.font = fontTable[MESSAGE_FONT][squareSize];
927 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
928 appData.coordFont = fontTable[COORD_FONT][squareSize];
931 appData.font = InsertPxlSize(appData.font, fontPxlSize);
932 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
933 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
934 fontSet = CreateFontSet(appData.font);
935 clockFontSet = CreateFontSet(appData.clockFont);
937 /* For the coordFont, use the 0th font of the fontset. */
938 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
939 XFontStruct **font_struct_list;
940 XFontSetExtents *fontSize;
941 char **font_name_list;
942 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
943 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
944 coordFontStruct = XQueryFont(xDisplay, coordFontID);
945 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
946 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
949 appData.font = FindFont(appData.font, fontPxlSize);
950 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
951 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
952 clockFontID = XLoadFont(xDisplay, appData.clockFont);
953 clockFontStruct = XQueryFont(xDisplay, clockFontID);
954 coordFontID = XLoadFont(xDisplay, appData.coordFont);
955 coordFontStruct = XQueryFont(xDisplay, coordFontID);
956 // textHeight in !NLS mode!
958 countFontID = coordFontID; // [HGM] holdings
959 countFontStruct = coordFontStruct;
961 xdb = XtDatabase(xDisplay);
963 XrmPutLineResource(&xdb, "*international: True");
964 vTo.size = sizeof(XFontSet);
965 vTo.addr = (XtPointer) &fontSet;
966 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
968 XrmPutStringResource(&xdb, "*font", appData.font);
979 case ArgInt: p = " N"; break;
980 case ArgString: p = " STR"; break;
981 case ArgBoolean: p = " TF"; break;
982 case ArgSettingsFilename:
983 case ArgFilename: p = " FILE"; break;
984 case ArgX: p = " Nx"; break;
985 case ArgY: p = " Ny"; break;
986 case ArgAttribs: p = " TEXTCOL"; break;
987 case ArgColor: p = " COL"; break;
988 case ArgFont: p = " FONT"; break;
989 case ArgBoardSize: p = " SIZE"; break;
990 case ArgFloat: p = " FLOAT"; break;
995 case ArgCommSettings:
1006 ArgDescriptor *q, *p = argDescriptors+5;
1007 printf("\nXBoard accepts the following options:\n"
1008 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1009 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1010 " SIZE = board-size spec(s)\n"
1011 " Within parentheses are short forms, or options to set to true or false.\n"
1012 " Persistent options (saved in the settings file) are marked with *)\n\n");
1014 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1015 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1016 if(p->save) strcat(buf+len, "*");
1017 for(q=p+1; q->argLoc == p->argLoc; q++) {
1018 if(q->argName[0] == '-') continue;
1019 strcat(buf+len, q == p+1 ? " (" : " ");
1020 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1022 if(q != p+1) strcat(buf+len, ")");
1024 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1027 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1031 main (int argc, char **argv)
1033 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1035 XSetWindowAttributes window_attributes;
1037 Dimension boardWidth, boardHeight, w, h;
1040 int boardWidth, boardHeight, w, h;
1042 int forceMono = False;
1043 GError *gtkerror=NULL;
1045 srandom(time(0)); // [HGM] book: make random truly random
1047 setbuf(stdout, NULL);
1048 setbuf(stderr, NULL);
1051 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1052 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1056 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1062 gtk_init (&argc, &argv);
1064 programName = strrchr(argv[0], '/');
1065 if (programName == NULL)
1066 programName = argv[0];
1071 // if (appData.debugMode) {
1072 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1075 bindtextdomain(PACKAGE, LOCALEDIR);
1076 textdomain(PACKAGE);
1079 appData.boardSize = "";
1080 InitAppData(ConvertToLine(argc, argv));
1082 if (p == NULL) p = "/tmp";
1083 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1084 gameCopyFilename = (char*) malloc(i);
1085 gamePasteFilename = (char*) malloc(i);
1086 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1087 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1089 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1090 static char buf[MSG_SIZ];
1091 EscapeExpand(buf, appData.firstInitString);
1092 appData.firstInitString = strdup(buf);
1093 EscapeExpand(buf, appData.secondInitString);
1094 appData.secondInitString = strdup(buf);
1095 EscapeExpand(buf, appData.firstComputerString);
1096 appData.firstComputerString = strdup(buf);
1097 EscapeExpand(buf, appData.secondComputerString);
1098 appData.secondComputerString = strdup(buf);
1101 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1104 if (chdir(chessDir) != 0) {
1105 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1111 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1112 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1113 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1114 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1117 setbuf(debugFP, NULL);
1121 if (appData.debugMode) {
1122 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1126 /* [HGM,HR] make sure board size is acceptable */
1127 if(appData.NrFiles > BOARD_FILES ||
1128 appData.NrRanks > BOARD_RANKS )
1129 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1132 /* This feature does not work; animation needs a rewrite */
1133 appData.highlightDragging = FALSE;
1137 gameInfo.variant = StringToVariant(appData.variant);
1138 InitPosition(FALSE);
1142 builder = gtk_builder_new();
1143 filename = get_glade_filename ("mainboard.glade");
1144 if(! gtk_builder_add_from_file (builder, filename, >kerror) )
1147 printf ("Error: %d %s\n",gtkerror->code,gtkerror->message);
1149 mainwindow = GTK_WIDGET(gtk_builder_get_object (builder, "mainwindow"));
1152 XtAppInitialize(&appContext, "XBoard", shellOptions,
1153 XtNumber(shellOptions),
1154 &argc, argv, xboardResources, NULL, 0);
1156 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1157 clientResources, XtNumber(clientResources),
1160 xDisplay = XtDisplay(shellWidget);
1161 xScreen = DefaultScreen(xDisplay);
1162 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1166 * determine size, based on supplied or remembered -size, or screen size
1168 if (isdigit(appData.boardSize[0])) {
1169 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1170 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1171 &fontPxlSize, &smallLayout, &tinyLayout);
1173 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1174 programName, appData.boardSize);
1178 /* Find some defaults; use the nearest known size */
1179 SizeDefaults *szd, *nearest;
1180 int distance = 99999;
1181 nearest = szd = sizeDefaults;
1182 while (szd->name != NULL) {
1183 if (abs(szd->squareSize - squareSize) < distance) {
1185 distance = abs(szd->squareSize - squareSize);
1186 if (distance == 0) break;
1190 if (i < 2) lineGap = nearest->lineGap;
1191 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1192 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1193 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1194 if (i < 6) smallLayout = nearest->smallLayout;
1195 if (i < 7) tinyLayout = nearest->tinyLayout;
1198 SizeDefaults *szd = sizeDefaults;
1199 if (*appData.boardSize == NULLCHAR) {
1201 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1202 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1206 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
1207 guint screenwidth = gdk_screen_get_width(screen);
1208 guint screenheight = gdk_screen_get_height(screen);
1209 while (screenwidth < szd->minScreenSize ||
1210 screenheight < szd->minScreenSize) {
1214 if (szd->name == NULL) szd--;
1215 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1217 while (szd->name != NULL &&
1218 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1219 if (szd->name == NULL) {
1220 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1221 programName, appData.boardSize);
1225 squareSize = szd->squareSize;
1226 lineGap = szd->lineGap;
1227 clockFontPxlSize = szd->clockFontPxlSize;
1228 coordFontPxlSize = szd->coordFontPxlSize;
1229 fontPxlSize = szd->fontPxlSize;
1230 smallLayout = szd->smallLayout;
1231 tinyLayout = szd->tinyLayout;
1232 // [HGM] font: use defaults from settings file if available and not overruled
1235 defaultLineGap = lineGap;
1236 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1238 /* [HR] height treated separately (hacked) */
1239 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1240 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1243 * Determine what fonts to use.
1246 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1250 * Detect if there are not enough colors available and adapt.
1253 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1254 appData.monoMode = True;
1258 forceMono = MakeColors();
1261 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1263 appData.monoMode = True;
1266 ParseIcsTextColors();
1269 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1276 layoutName = "tinyLayout";
1277 } else if (smallLayout) {
1278 layoutName = "smallLayout";
1280 layoutName = "normalLayout";
1283 optList = BoardPopUp(squareSize, lineGap, (void*)
1293 InitDrawingHandle(optList + W_BOARD);
1294 shellWidget = shells[BoardWindow];
1295 currBoard = &optList[W_BOARD];
1296 boardWidget = optList[W_BOARD].handle;
1297 menuBarWidget = optList[W_MENU].handle;
1298 dropMenu = optList[W_DROP].handle;
1299 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1301 formWidget = XtParent(boardWidget);
1302 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1303 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1304 XtGetValues(optList[W_WHITE].handle, args, 2);
1305 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1306 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1307 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1308 XtGetValues(optList[W_PAUSE].handle, args, 2);
1313 xBoardWindow = XtWindow(boardWidget);
1316 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1317 // not need to go into InitDrawingSizes().
1322 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1324 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
1325 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
1326 mainwindowIcon = WhiteIcon;
1327 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1331 * Create a cursor for the board widget.
1334 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1335 XChangeWindowAttributes(xDisplay, xBoardWindow,
1336 CWCursor, &window_attributes);
1340 * Inhibit shell resizing.
1343 shellArgs[0].value = (XtArgVal) &w;
1344 shellArgs[1].value = (XtArgVal) &h;
1345 XtGetValues(shellWidget, shellArgs, 2);
1346 shellArgs[4].value = shellArgs[2].value = w;
1347 shellArgs[5].value = shellArgs[3].value = h;
1348 // XtSetValues(shellWidget, &shellArgs[2], 4);
1352 gtk_widget_get_allocation(shells[BoardWindow], &a);
1353 w = a.width; h = a.height;
1354 printf("start size (%d,%d), %dx%d\n", a.x, a.y, w, h);
1356 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1357 marginH = h - boardHeight;
1360 CatchDeleteWindow(shellWidget, "QuitProc");
1366 if(appData.logoSize)
1367 { // locate and read user logo
1369 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1370 ASSIGN(userLogo, buf);
1373 if (appData.animate || appData.animateDragging)
1377 XtAugmentTranslations(formWidget,
1378 XtParseTranslationTable(globalTranslations));
1380 XtAddEventHandler(formWidget, KeyPressMask, False,
1381 (XtEventHandler) MoveTypeInProc, NULL);
1382 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1383 (XtEventHandler) EventProc, NULL);
1385 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1386 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1388 /* [AS] Restore layout */
1389 if( wpMoveHistory.visible ) {
1393 if( wpEvalGraph.visible )
1398 if( wpEngineOutput.visible ) {
1399 EngineOutputPopUp();
1404 if (errorExitStatus == -1) {
1405 if (appData.icsActive) {
1406 /* We now wait until we see "login:" from the ICS before
1407 sending the logon script (problems with timestamp otherwise) */
1408 /*ICSInitScript();*/
1409 if (appData.icsInputBox) ICSInputBoxPopUp();
1413 signal(SIGWINCH, TermSizeSigHandler);
1415 signal(SIGINT, IntSigHandler);
1416 signal(SIGTERM, IntSigHandler);
1417 if (*appData.cmailGameName != NULLCHAR) {
1418 signal(SIGUSR1, CmailSigHandler);
1422 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1425 // XtSetKeyboardFocus(shellWidget, formWidget);
1427 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1430 /* check for GTK events and process them */
1433 gtk_main_iteration();
1436 if (appData.debugMode) fclose(debugFP); // [DM] debug
1441 TermSizeSigHandler (int sig)
1447 IntSigHandler (int sig)
1453 CmailSigHandler (int sig)
1458 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1460 /* Activate call-back function CmailSigHandlerCallBack() */
1461 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1463 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1467 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1470 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1472 /**** end signal code ****/
1475 #define Abs(n) ((n)<0 ? -(n) : (n))
1479 InsertPxlSize (char *pattern, int targetPxlSize)
1481 char *base_fnt_lst, strInt[12], *p, *q;
1482 int alternatives, i, len, strIntLen;
1485 * Replace the "*" (if present) in the pixel-size slot of each
1486 * alternative with the targetPxlSize.
1490 while ((p = strchr(p, ',')) != NULL) {
1494 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1495 strIntLen = strlen(strInt);
1496 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1500 while (alternatives--) {
1501 char *comma = strchr(p, ',');
1502 for (i=0; i<14; i++) {
1503 char *hyphen = strchr(p, '-');
1505 if (comma && hyphen > comma) break;
1506 len = hyphen + 1 - p;
1507 if (i == 7 && *p == '*' && len == 2) {
1509 memcpy(q, strInt, strIntLen);
1519 len = comma + 1 - p;
1526 return base_fnt_lst;
1531 CreateFontSet (char *base_fnt_lst)
1534 char **missing_list;
1538 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1539 &missing_list, &missing_count, &def_string);
1540 if (appData.debugMode) {
1542 XFontStruct **font_struct_list;
1543 char **font_name_list;
1544 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1546 fprintf(debugFP, " got list %s, locale %s\n",
1547 XBaseFontNameListOfFontSet(fntSet),
1548 XLocaleOfFontSet(fntSet));
1549 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1550 for (i = 0; i < count; i++) {
1551 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1554 for (i = 0; i < missing_count; i++) {
1555 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1558 if (fntSet == NULL) {
1559 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1565 #else // not ENABLE_NLS
1567 * Find a font that matches "pattern" that is as close as
1568 * possible to the targetPxlSize. Prefer fonts that are k
1569 * pixels smaller to fonts that are k pixels larger. The
1570 * pattern must be in the X Consortium standard format,
1571 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1572 * The return value should be freed with XtFree when no
1576 FindFont (char *pattern, int targetPxlSize)
1578 char **fonts, *p, *best, *scalable, *scalableTail;
1579 int i, j, nfonts, minerr, err, pxlSize;
1582 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1584 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1585 programName, pattern);
1592 for (i=0; i<nfonts; i++) {
1595 if (*p != '-') continue;
1597 if (*p == NULLCHAR) break;
1598 if (*p++ == '-') j++;
1600 if (j < 7) continue;
1603 scalable = fonts[i];
1606 err = pxlSize - targetPxlSize;
1607 if (Abs(err) < Abs(minerr) ||
1608 (minerr > 0 && err < 0 && -err == minerr)) {
1614 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1615 /* If the error is too big and there is a scalable font,
1616 use the scalable font. */
1617 int headlen = scalableTail - scalable;
1618 p = (char *) XtMalloc(strlen(scalable) + 10);
1619 while (isdigit(*scalableTail)) scalableTail++;
1620 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1622 p = (char *) XtMalloc(strlen(best) + 2);
1623 safeStrCpy(p, best, strlen(best)+1 );
1625 if (appData.debugMode) {
1626 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1627 pattern, targetPxlSize, p);
1629 XFreeFontNames(fonts);
1636 EnableNamedMenuItem (char *menuRef, int state)
1638 MenuItem *item = MenuNameToItem(menuRef);
1640 if(item) gtk_widget_set_sensitive(item->handle, state);
1644 EnableButtonBar (int state)
1647 XtSetSensitive(optList[W_BUTTON].handle, state);
1653 SetMenuEnables (Enables *enab)
1655 while (enab->name != NULL) {
1656 EnableNamedMenuItem(enab->name, enab->value);
1661 gboolean KeyPressProc(window, eventkey, data)
1663 GdkEventKey *eventkey;
1667 MoveTypeInProc(eventkey); // pop up for typed in moves
1670 /* check for other key values */
1671 switch(eventkey->keyval) {
1683 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1684 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1686 if(*nprms == 0) return;
1687 item = MenuNameToItem(prms[0]);
1688 if(item) ((MenuProc *) item->proc) ();
1702 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1703 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1704 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1705 dmEnables[i].piece);
1706 XtSetSensitive(entry, p != NULL || !appData.testLegality
1707 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1708 && !appData.icsActive));
1710 while (p && *p++ == dmEnables[i].piece) count++;
1711 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1713 XtSetArg(args[j], XtNlabel, label); j++;
1714 XtSetValues(entry, args, j);
1720 do_flash_delay (unsigned long msec)
1726 FlashDelay (int flash_delay)
1728 if(flash_delay) do_flash_delay(flash_delay);
1732 Fraction (int x, int start, int stop)
1734 double f = ((double) x - start)/(stop - start);
1735 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1739 static WindowPlacement wpNew;
1742 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1744 int j=0, touch=0, fudge = 2;
1745 GetActualPlacement(sh, wp);
1746 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1747 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1748 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1749 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1750 if(!touch ) return; // only windows that touch co-move
1751 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1752 int heightInc = wpNew.height - wpMain.height;
1753 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1754 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1755 wp->y += fracTop * heightInc;
1756 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1758 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1760 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1761 int widthInc = wpNew.width - wpMain.width;
1762 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1763 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1764 wp->y += fracLeft * widthInc;
1765 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1767 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1770 wp->x += wpNew.x - wpMain.x;
1771 wp->y += wpNew.y - wpMain.y;
1772 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1773 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1775 XtSetArg(args[j], XtNx, wp->x); j++;
1776 XtSetArg(args[j], XtNy, wp->y); j++;
1777 XtSetValues(sh, args, j);
1782 ReSize (WindowPlacement *wp)
1785 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1786 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1787 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1788 if(sqy < sqx) sqx = sqy;
1789 if(sqx != squareSize) {
1790 squareSize = sqx; // adopt new square size
1791 CreatePNGPieces(); // make newly scaled pieces
1792 InitDrawingSizes(0, 0); // creates grid etc.
1793 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1794 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1795 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1796 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1797 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1801 static XtIntervalId delayedDragID = 0;
1803 static guint delayedDragTag = 0;
1813 // GetActualPlacement(shellWidget, &wpNew);
1814 printf("drag proc (%d,%d) %dx%d\n", wpNew.x, wpNew.y, wpNew.width, wpNew.height);
1815 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1816 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1817 busy = 0; return; // false alarm
1820 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1821 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1822 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1823 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1825 DrawPosition(True, NULL);
1826 if(delayedDragTag) g_source_remove(delayedDragTag);
1827 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1834 printf("old timr = %d\n", delayedDragTag);
1835 if(delayedDragTag) g_source_remove(delayedDragTag);
1836 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1837 printf("new timr = %d\n", delayedDragTag);
1841 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1843 printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1845 wpNew.x = event->configure.x;
1846 wpNew.y = event->configure.y;
1847 wpNew.width = event->configure.width;
1848 wpNew.height = event->configure.height;
1849 if(appData.useStickyWindows)
1850 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1855 * event handler for redrawing the board
1859 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1861 DrawPosition(True, NULL);
1867 /* Disable all user input other than deleting the window */
1868 static int frozen = 0;
1874 /* Grab by a widget that doesn't accept input */
1875 gtk_grab_add(optList[W_MESSG].handle);
1879 /* Undo a FreezeUI */
1883 if (!frozen) return;
1884 gtk_grab_remove(optList[W_MESSG].handle);
1891 static int oldPausing = FALSE;
1892 static GameMode oldmode = (GameMode) -1;
1894 if (!boardWidget) return;
1896 if (pausing != oldPausing) {
1897 oldPausing = pausing;
1898 MarkMenuItem("Mode.Pause", pausing);
1900 if (appData.showButtonBar) {
1901 /* Always toggle, don't set. Previous code messes up when
1902 invoked while the button is pressed, as releasing it
1903 toggles the state again. */
1905 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1906 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1910 wname = ModeToWidgetName(oldmode);
1911 if (wname != NULL) {
1912 MarkMenuItem(wname, False);
1914 wname = ModeToWidgetName(gameMode);
1915 if (wname != NULL) {
1916 MarkMenuItem(wname, True);
1919 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1921 /* Maybe all the enables should be handled here, not just this one */
1922 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1924 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1929 * Button/menu procedures
1933 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1934 char *selected_fen_position=NULL;
1937 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1938 Atom *type_return, XtPointer *value_return,
1939 unsigned long *length_return, int *format_return)
1941 char *selection_tmp;
1943 // if (!selected_fen_position) return False; /* should never happen */
1944 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1945 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1946 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1949 if (f == NULL) return False;
1953 selection_tmp = XtMalloc(len + 1);
1954 count = fread(selection_tmp, 1, len, f);
1957 XtFree(selection_tmp);
1960 selection_tmp[len] = NULLCHAR;
1962 /* note: since no XtSelectionDoneProc was registered, Xt will
1963 * automatically call XtFree on the value returned. So have to
1964 * make a copy of it allocated with XtMalloc */
1965 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1966 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1969 *value_return=selection_tmp;
1970 *length_return=strlen(selection_tmp);
1971 *type_return=*target;
1972 *format_return = 8; /* bits per byte */
1974 } else if (*target == XA_TARGETS(xDisplay)) {
1975 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1976 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1977 targets_tmp[1] = XA_STRING;
1978 *value_return = targets_tmp;
1979 *type_return = XA_ATOM;
1982 // This code leads to a read of value_return out of bounds on 64-bit systems.
1983 // Other code which I have seen always sets *format_return to 32 independent of
1984 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1985 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1986 *format_return = 8 * sizeof(Atom);
1987 if (*format_return > 32) {
1988 *length_return *= *format_return / 32;
1989 *format_return = 32;
1992 *format_return = 32;
2001 /* note: when called from menu all parameters are NULL, so no clue what the
2002 * Widget which was clicked on was, or what the click event was
2005 CopySomething (char *src)
2008 selected_fen_position = src;
2010 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2011 * have a notion of a position that is selected but not copied.
2012 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2014 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2016 SendPositionSelection,
2017 NULL/* lose_ownership_proc */ ,
2018 NULL/* transfer_done_proc */);
2019 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2021 SendPositionSelection,
2022 NULL/* lose_ownership_proc */ ,
2023 NULL/* transfer_done_proc */);
2028 /* function called when the data to Paste is ready */
2030 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2031 Atom *type, XtPointer value, unsigned long *len, int *format)
2034 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2035 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2036 EditPositionPasteFEN(fenstr);
2041 /* called when Paste Position button is pressed,
2042 * all parameters will be NULL */
2044 PastePositionProc ()
2047 XtGetSelectionValue(menuBarWidget,
2048 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2049 /* (XtSelectionCallbackProc) */ PastePositionCB,
2050 NULL, /* client_data passed to PastePositionCB */
2052 /* better to use the time field from the event that triggered the
2053 * call to this function, but that isn't trivial to get
2062 /* note: when called from menu all parameters are NULL, so no clue what the
2063 * Widget which was clicked on was, or what the click event was
2065 /* function called when the data to Paste is ready */
2067 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2068 Atom *type, XtPointer value, unsigned long *len, int *format)
2071 if (value == NULL || *len == 0) {
2072 return; /* nothing had been selected to copy */
2074 f = fopen(gamePasteFilename, "w");
2076 DisplayError(_("Can't open temp file"), errno);
2079 fwrite(value, 1, *len, f);
2082 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2086 /* called when Paste Game button is pressed,
2087 * all parameters will be NULL */
2092 XtGetSelectionValue(menuBarWidget,
2093 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2094 /* (XtSelectionCallbackProc) */ PasteGameCB,
2095 NULL, /* client_data passed to PasteGameCB */
2097 /* better to use the time field from the event that triggered the
2098 * call to this function, but that isn't trivial to get
2109 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2115 void MoveTypeInProc(eventkey)
2116 GdkEventKey *eventkey;
2120 // ingnore if ctrl or alt is pressed
2121 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
2125 buf[0]=eventkey->keyval;
2133 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2135 if (!TempBackwardActive) {
2136 TempBackwardActive = True;
2142 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2144 /* Check to see if triggered by a key release event for a repeating key.
2145 * If so the next queued event will be a key press of the same key at the same time */
2146 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2148 XPeekEvent(xDisplay, &next);
2149 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2150 next.xkey.keycode == event->xkey.keycode)
2154 TempBackwardActive = False;
2158 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2159 { // called as key binding
2162 if (nprms && *nprms > 0)
2166 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2173 { // called from menu
2175 ManInner(NULL, NULL, NULL, NULL);
2180 SetWindowTitle (char *text, char *title, char *icon)
2185 if (appData.titleInWindow) {
2187 XtSetArg(args[i], XtNlabel, text); i++;
2188 XtSetValues(titleWidget, args, i);
2191 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2192 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2193 XtSetValues(shellWidget, args, i);
2194 XSync(xDisplay, False);
2196 if (appData.titleInWindow) {
2197 SetWidgetLabel(titleWidget, text);
2199 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2204 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2210 DisplayIcsInteractionTitle (String message)
2213 if (oldICSInteractionTitle == NULL) {
2214 /* Magic to find the old window title, adapted from vim */
2215 char *wina = getenv("WINDOWID");
2217 Window win = (Window) atoi(wina);
2218 Window root, parent, *children;
2219 unsigned int nchildren;
2220 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2222 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2223 if (!XQueryTree(xDisplay, win, &root, &parent,
2224 &children, &nchildren)) break;
2225 if (children) XFree((void *)children);
2226 if (parent == root || parent == 0) break;
2229 XSetErrorHandler(oldHandler);
2231 if (oldICSInteractionTitle == NULL) {
2232 oldICSInteractionTitle = "xterm";
2235 printf("\033]0;%s\007", message);
2242 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2244 GtkWidget *w = (GtkWidget *) opt->handle;
2250 strcpy(bgcolor, "black");
2251 strcpy(fgcolor, "white");
2253 strcpy(bgcolor, "white");
2254 strcpy(fgcolor, "black");
2257 appData.lowTimeWarning &&
2258 (timer / 1000) < appData.icsAlarmTime) {
2259 strcpy(fgcolor, appData.lowTimeWarningColor);
2262 if (appData.clockMode) {
2263 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2264 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2266 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2267 bgcolor, fgcolor, color);
2269 gtk_label_set_markup(GTK_LABEL(w), markup);
2273 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2276 SetClockIcon (int color)
2278 GdkPixbuf *pm = *clockIcons[color];
2279 if (mainwindowIcon != pm) {
2280 mainwindowIcon = pm;
2281 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2285 #define INPUT_SOURCE_BUF_SIZE 8192
2294 char buf[INPUT_SOURCE_BUF_SIZE];
2299 DoInputCallback(io, cond, data)
2304 /* read input from one of the input source (for example a chess program, ICS, etc).
2305 * and call a function that will handle the input
2312 /* All information (callback function, file descriptor, etc) is
2313 * saved in an InputSource structure
2315 InputSource *is = (InputSource *) data;
2317 if (is->lineByLine) {
2318 count = read(is->fd, is->unused,
2319 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2321 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2324 is->unused += count;
2326 /* break input into lines and call the callback function on each
2329 while (p < is->unused) {
2330 q = memchr(p, '\n', is->unused - p);
2331 if (q == NULL) break;
2333 (is->func)(is, is->closure, p, q - p, 0);
2336 /* remember not yet used part of the buffer */
2338 while (p < is->unused) {
2343 /* read maximum length of input buffer and send the whole buffer
2344 * to the callback function
2346 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2351 (is->func)(is, is->closure, is->buf, count, error);
2353 return True; // Must return true or the watch will be removed
2356 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2363 GIOChannel *channel;
2364 ChildProc *cp = (ChildProc *) pr;
2366 is = (InputSource *) calloc(1, sizeof(InputSource));
2367 is->lineByLine = lineByLine;
2371 is->fd = fileno(stdin);
2373 is->kind = cp->kind;
2374 is->fd = cp->fdFrom;
2377 is->unused = is->buf;
2381 /* GTK-TODO: will this work on windows?*/
2383 channel = g_io_channel_unix_new(is->fd);
2384 g_io_channel_set_close_on_unref (channel, TRUE);
2385 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2387 is->closure = closure;
2388 return (InputSourceRef) is;
2393 RemoveInputSource(isr)
2396 InputSource *is = (InputSource *) isr;
2398 if (is->sid == 0) return;
2399 g_source_remove(is->sid);
2406 static Boolean frameWaiting;
2409 FrameAlarm (int sig)
2411 frameWaiting = False;
2412 /* In case System-V style signals. Needed?? */
2413 signal(SIGALRM, FrameAlarm);
2417 FrameDelay (int time)
2419 struct itimerval delay;
2422 frameWaiting = True;
2423 signal(SIGALRM, FrameAlarm);
2424 delay.it_interval.tv_sec =
2425 delay.it_value.tv_sec = time / 1000;
2426 delay.it_interval.tv_usec =
2427 delay.it_value.tv_usec = (time % 1000) * 1000;
2428 setitimer(ITIMER_REAL, &delay, NULL);
2429 while (frameWaiting) pause();
2430 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2431 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2432 setitimer(ITIMER_REAL, &delay, NULL);
2439 FrameDelay (int time)
2442 XSync(xDisplay, False);
2444 // gtk_main_iteration_do(False);
2447 usleep(time * 1000);
2453 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2455 char buf[MSG_SIZ], *logoName = buf;
2456 if(appData.logo[n][0]) {
2457 logoName = appData.logo[n];
2458 } else if(appData.autoLogo) {
2459 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2460 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2461 } else if(appData.directory[n] && appData.directory[n][0]) {
2462 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2466 { ASSIGN(cps->programLogo, logoName); }
2470 UpdateLogos (int displ)
2472 if(optList[W_WHITE-1].handle == NULL) return;
2473 LoadLogo(&first, 0, 0);
2474 LoadLogo(&second, 1, appData.icsActive);
2475 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2479 void FileNamePopUpGTK(label, def, filter, proc, pathFlag, openMode, name, fp)
2490 GtkFileFilter *gtkfilter;
2491 GtkFileFilter *gtkfilter_all;
2493 char fileext[10] = "";
2494 char *result = NULL;
2497 /* make a copy of the filter string, so that strtok can work with it*/
2498 cp = strndup(filter,strlen(filter));
2500 /* add filters for file extensions */
2501 gtkfilter = gtk_file_filter_new();
2502 gtkfilter_all = gtk_file_filter_new();
2504 /* one filter to show everything */
2505 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2506 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2508 /* add filter if present */
2509 result = strtok(cp, space);
2510 while( result != NULL ) {
2511 snprintf(fileext,10,"*%s",result);
2512 result = strtok( NULL, space );
2513 gtk_file_filter_add_pattern(gtkfilter, fileext);
2516 /* second filter to only show what's useful */
2517 gtk_file_filter_set_name (gtkfilter,filter);
2519 if (openMode[0] == 'r')
2521 dialog = gtk_file_chooser_dialog_new (label,
2523 GTK_FILE_CHOOSER_ACTION_OPEN,
2524 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2525 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2530 dialog = gtk_file_chooser_dialog_new (label,
2532 GTK_FILE_CHOOSER_ACTION_SAVE,
2533 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2534 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2536 /* add filename suggestions */
2537 if (strlen(def) > 0 )
2538 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2540 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2544 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2545 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2546 /* activate filter */
2547 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2549 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2554 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2557 f = fopen(filename, openMode);
2560 DisplayError(_("Failed to open file"), errno);
2564 /* TODO add indec */
2566 ASSIGN(*name, filename);
2567 ScheduleDelayedEvent(DelayedLoad, 50);
2572 gtk_widget_destroy (dialog);