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, 2015, 2016 Free
9 * Software Foundation, Inc.
11 * The following terms apply to Digital Equipment Corporation's copyright
13 * ------------------------------------------------------------------------
16 * Permission to use, copy, modify, and distribute this software and its
17 * documentation for any purpose and without fee is hereby granted,
18 * provided that the above copyright notice appear in all copies and that
19 * both that copyright notice and this permission notice appear in
20 * supporting documentation, and that the name of Digital not be
21 * used in advertising or publicity pertaining to distribution of the
22 * software without specific, written prior permission.
24 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
26 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
27 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
28 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
29 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
31 * ------------------------------------------------------------------------
33 * The following terms apply to the enhanced version of XBoard
34 * distributed by the Free Software Foundation:
35 * ------------------------------------------------------------------------
37 * GNU XBoard is free software: you can redistribute it and/or modify
38 * it under the terms of the GNU General Public License as published by
39 * the Free Software Foundation, either version 3 of the License, or (at
40 * your option) any later version.
42 * GNU XBoard is distributed in the hope that it will be useful, but
43 * WITHOUT ANY WARRANTY; without even the implied warranty of
44 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
45 * General Public License for more details.
47 * You should have received a copy of the GNU General Public License
48 * along with this program. If not, see http://www.gnu.org/licenses/. *
50 *------------------------------------------------------------------------
51 ** See the file ChangeLog for a revision history. */
61 #include <sys/types.h>
65 #include <cairo/cairo.h>
66 #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 #include <X11/Intrinsic.h>
153 #include <X11/StringDefs.h>
154 #include <X11/Shell.h>
155 #include <X11/cursorfont.h>
156 #include <X11/Xatom.h>
157 #include <X11/Xmu/Atoms.h>
159 #include <X11/Xaw3d/Dialog.h>
160 #include <X11/Xaw3d/Form.h>
161 #include <X11/Xaw3d/List.h>
162 #include <X11/Xaw3d/Label.h>
163 #include <X11/Xaw3d/SimpleMenu.h>
164 #include <X11/Xaw3d/SmeBSB.h>
165 #include <X11/Xaw3d/SmeLine.h>
166 #include <X11/Xaw3d/Box.h>
167 #include <X11/Xaw3d/MenuButton.h>
168 #include <X11/Xaw3d/Text.h>
169 #include <X11/Xaw3d/AsciiText.h>
171 #include <X11/Xaw/Dialog.h>
172 #include <X11/Xaw/Form.h>
173 #include <X11/Xaw/List.h>
174 #include <X11/Xaw/Label.h>
175 #include <X11/Xaw/SimpleMenu.h>
176 #include <X11/Xaw/SmeBSB.h>
177 #include <X11/Xaw/SmeLine.h>
178 #include <X11/Xaw/Box.h>
179 #include <X11/Xaw/MenuButton.h>
180 #include <X11/Xaw/Text.h>
181 #include <X11/Xaw/AsciiText.h>
184 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
187 #include "bitmaps/icon_white.bm"
188 #include "bitmaps/icon_black.bm"
189 #include "bitmaps/checkmark.bm"
191 #include "frontend.h"
193 #include "backendz.h"
197 #include "xgamelist.h"
198 #include "xhistory.h"
202 #include "engineoutput.h"
213 #define usleep(t) _sleep2(((t)+500)/1000)
217 # define _(s) gettext (s)
218 # define N_(s) gettext_noop (s)
224 int main P((int argc, char **argv));
225 RETSIGTYPE CmailSigHandler P((int sig));
226 RETSIGTYPE IntSigHandler P((int sig));
227 RETSIGTYPE TermSizeSigHandler P((int sig));
228 Widget CreateMenuBar P((Menu *mb, int boardWidth));
230 char *InsertPxlSize P((char *pattern, int targetPxlSize));
231 XFontSet CreateFontSet P((char *base_fnt_lst));
233 char *FindFont P((char *pattern, int targetPxlSize));
235 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
236 u_int wreq, u_int hreq));
237 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
238 void DelayedDrag P((void));
239 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
240 void HandlePV P((Widget w, XEvent * event,
241 String * params, Cardinal * nParams));
242 void DrawPositionProc P((Widget w, XEvent *event,
243 String *prms, Cardinal *nprms));
244 void CommentClick P((Widget w, XEvent * event,
245 String * params, Cardinal * nParams));
246 void ICSInputBoxPopUp P((void));
247 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
248 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
249 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
250 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
251 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
252 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
253 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
254 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
255 Boolean TempBackwardActive = False;
256 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
257 void DisplayMove P((int moveNumber));
258 void update_ics_width P(());
259 int CopyMemoProc P(());
260 static int FindLogo P((char *place, char *name, char *buf));
263 * XBoard depends on Xt R4 or higher
265 int xtVersion = XtSpecificationRelease;
270 Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
271 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
272 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
273 Option *optList; // contains all widgets of main window
275 XFontSet fontSet, clockFontSet;
278 XFontStruct *clockFontStruct;
280 Font coordFontID, countFontID;
281 XFontStruct *coordFontStruct, *countFontStruct;
282 XtAppContext appContext;
285 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
287 Position commentX = -1, commentY = -1;
288 Dimension commentW, commentH;
289 typedef unsigned int BoardSize;
291 Boolean chessProgram;
293 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
294 int smallLayout = 0, tinyLayout = 0,
295 marginW, marginH, // [HGM] for run-time resizing
296 fromX = -1, fromY = -1, toX, toY, commentUp = False,
297 errorExitStatus = -1, defaultLineGap;
298 Dimension textHeight;
299 Pixel timerForegroundPixel, timerBackgroundPixel;
300 Pixel buttonForegroundPixel, buttonBackgroundPixel;
301 char *chessDir, *programName, *programVersion;
302 Boolean alwaysOnTop = False;
303 char *icsTextMenuString;
305 char *firstChessProgramNames;
306 char *secondChessProgramNames;
308 WindowPlacement wpMain;
309 WindowPlacement wpConsole;
310 WindowPlacement wpComment;
311 WindowPlacement wpMoveHistory;
312 WindowPlacement wpEvalGraph;
313 WindowPlacement wpEngineOutput;
314 WindowPlacement wpGameList;
315 WindowPlacement wpTags;
316 WindowPlacement wpDualBoard;
319 /* This magic number is the number of intermediate frames used
320 in each half of the animation. For short moves it's reduced
321 by 1. The total number of frames will be factor * 2 + 1. */
324 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
331 DropMenuEnables dmEnables[] = {
348 XtResource clientResources[] = {
349 { "flashCount", "flashCount", XtRInt, sizeof(int),
350 XtOffset(AppDataPtr, flashCount), XtRImmediate,
351 (XtPointer) FLASH_COUNT },
354 XrmOptionDescRec shellOptions[] = {
355 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
356 { "-flash", "flashCount", XrmoptionNoArg, "3" },
357 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
360 XtActionsRec boardActions[] = {
361 { "DrawPosition", DrawPositionProc },
362 { "HandlePV", HandlePV },
363 { "SelectPV", SelectPV },
364 { "StopPV", StopPV },
365 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
366 { "QuitProc", QuitWrapper },
367 { "ManProc", ManInner },
368 { "TempBackwardProc", TempBackwardProc },
369 { "TempForwardProc", TempForwardProc },
370 { "CommentClick", (XtActionProc) CommentClick },
371 { "GenericPopDown", (XtActionProc) GenericPopDown },
372 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
373 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
374 { "SelectMove", (XtActionProc) SelectMoveX },
375 { "LoadSelectedProc", LoadSelectedProc },
376 { "SetFilterProc", SetFilterProc },
377 { "TypeInProc", TypeInProc },
378 { "EnterKeyProc", EnterKeyProc },
379 { "UpKeyProc", UpKeyProc },
380 { "DownKeyProc", DownKeyProc },
381 { "WheelProc", WheelProc },
382 { "TabProc", TabProc },
385 char globalTranslations[] =
386 ":Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
387 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
388 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
389 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
390 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
391 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
392 :<Key>Pause: MenuItem(Mode.Pause) \n \
393 :Ctrl<Key>d: MenuItem(DebugProc) \n \
394 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
395 :<Key>Left: MenuItem(Edit.Backward) \n \
396 :<Key>Right: MenuItem(Edit.Forward) \n \
397 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
398 #ifndef OPTIONSDIALOG
400 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
401 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
402 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
403 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
404 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
407 :<KeyDown>Return: TempBackwardProc() \n \
408 :<KeyUp>Return: TempForwardProc() \n";
410 char ICSInputTranslations[] =
411 "<Key>Up: UpKeyProc() \n "
412 "<Key>Down: DownKeyProc() \n "
413 "<Key>Return: EnterKeyProc() \n";
415 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
416 // as the widget is destroyed before the up-click can call extend-end
417 char commentTranslations[] = "<Btn3Down>: extend-end(PRIMARY) select-start() CommentClick() \n";
419 String xboardResources[] = {
420 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
425 /* Max possible square size */
426 #define MAXSQSIZE 256
428 /* Arrange to catch delete-window events */
429 Atom wm_delete_window;
431 CatchDeleteWindow (Widget w, String procname)
434 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
435 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
436 XtAugmentTranslations(w, XtParseTranslationTable(buf));
443 XtSetArg(args[0], XtNiconic, False);
444 XtSetValues(shellWidget, args, 1);
446 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
449 //---------------------------------------------------------------------------------------------------------
450 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
453 #define CW_USEDEFAULT (1<<31)
454 #define ICS_TEXT_MENU_SIZE 90
455 #define DEBUG_FILE "xboard.debug"
456 #define SetCurrentDirectory chdir
457 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
461 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
464 // front-end part of option handling
466 // [HGM] This platform-dependent table provides the location for storing the color info
467 extern char *crWhite, * crBlack;
471 &appData.whitePieceColor,
472 &appData.blackPieceColor,
473 &appData.lightSquareColor,
474 &appData.darkSquareColor,
475 &appData.highlightSquareColor,
476 &appData.premoveHighlightColor,
477 &appData.lowTimeWarningColor,
488 // [HGM] font: keep a font for each square size, even non-stndard ones
491 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
492 char *fontTable[NUM_FONTS][MAX_SIZE];
495 ParseFont (char *name, int number)
496 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
498 if(sscanf(name, "size%d:", &size)) {
499 // [HGM] font: font is meant for specific boardSize (likely from settings file);
500 // defer processing it until we know if it matches our board size
501 if(strstr(name, "-*-") && // only pay attention to things that look like X-fonts
502 size >= 0 && size<MAX_SIZE) { // for now, fixed limit
503 fontTable[number][size] = strdup(strchr(name, ':')+1);
504 fontValid[number][size] = True;
509 case 0: // CLOCK_FONT
510 appData.clockFont = strdup(name);
512 case 1: // MESSAGE_FONT
513 appData.font = strdup(name);
515 case 2: // COORD_FONT
516 appData.coordFont = strdup(name);
521 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
526 { // only 2 fonts currently
527 appData.clockFont = CLOCK_FONT_NAME;
528 appData.coordFont = COORD_FONT_NAME;
529 appData.font = DEFAULT_FONT_NAME;
534 { // no-op, until we identify the code for this already in XBoard and move it here
538 ParseColor (int n, char *name)
539 { // in XBoard, just copy the color-name string
540 if(colorVariable[n] && *name == '#') *(char**)colorVariable[n] = strdup(name);
546 return *(char**)colorVariable[n];
550 ParseTextAttribs (ColorClass cc, char *s)
552 (&appData.colorShout)[cc] = strdup(s);
556 ParseBoardSize (void *addr, char *name)
558 appData.boardSize = strdup(name);
563 { // In XBoard the sound-playing program takes care of obtaining the actual sound
567 SetCommPortDefaults ()
568 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
571 // [HGM] args: these three cases taken out to stay in front-end
573 SaveFontArg (FILE *f, ArgDescriptor *ad)
576 int i, n = (int)(intptr_t)ad->argLoc;
578 case 0: // CLOCK_FONT
579 name = appData.clockFont;
581 case 1: // MESSAGE_FONT
584 case 2: // COORD_FONT
585 name = appData.coordFont;
590 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
591 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
592 fontTable[n][squareSize] = strdup(name);
593 fontValid[n][squareSize] = True;
596 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
597 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
602 { // nothing to do, as the sounds are at all times represented by their text-string names already
606 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
607 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
608 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
612 SaveColor (FILE *f, ArgDescriptor *ad)
613 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
614 if(colorVariable[(int)(intptr_t)ad->argLoc])
615 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
619 SaveBoardSize (FILE *f, char *name, void *addr)
620 { // wrapper to shield back-end from BoardSize & sizeInfo
621 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
625 ParseCommPortSettings (char *s)
626 { // no such option in XBoard (yet)
632 GetActualPlacement (Widget wg, WindowPlacement *wp)
634 XWindowAttributes winAt;
641 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
642 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
643 wp->x = rx - winAt.x;
644 wp->y = ry - winAt.y;
645 wp->height = winAt.height;
646 wp->width = winAt.width;
647 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
651 GetPlacement (DialogClass dlg, WindowPlacement *wp)
652 { // wrapper to shield back-end from widget type
653 if(shellUp[dlg]) GetActualPlacement(shells[dlg], wp);
658 { // wrapper to shield use of window handles from back-end (make addressible by number?)
659 // In XBoard this will have to wait until awareness of window parameters is implemented
660 GetActualPlacement(shellWidget, &wpMain);
661 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
662 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
663 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
664 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
665 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
666 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
670 PrintCommPortSettings (FILE *f, char *name)
671 { // This option does not exist in XBoard
675 EnsureOnScreen (int *x, int *y, int minX, int minY)
682 { // [HGM] args: allows testing if main window is realized from back-end
683 return xBoardWindow != 0;
687 PopUpStartupDialog ()
688 { // start menu not implemented in XBoard
692 ConvertToLine (int argc, char **argv)
694 static char line[128*1024], buf[1024];
698 for(i=1; i<argc; i++)
700 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
701 && argv[i][0] != '{' )
702 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
704 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
705 strncat(line, buf, 128*1024 - strlen(line) - 1 );
708 line[strlen(line)-1] = NULLCHAR;
712 //--------------------------------------------------------------------------------------------
715 ResizeBoardWindow (int w, int h, int inhibit)
717 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
719 shellArgs[0].value = w;
720 shellArgs[1].value = h;
721 shellArgs[4].value = shellArgs[2].value = w;
722 shellArgs[5].value = shellArgs[3].value = h;
723 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
725 XSync(xDisplay, False);
729 MakeOneColor (char *name, Pixel *color)
732 if (!appData.monoMode) {
733 vFrom.addr = (caddr_t) name;
734 vFrom.size = strlen(name);
735 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
736 if (vTo.addr == NULL) {
737 appData.monoMode = True;
740 *color = *(Pixel *) vTo.addr;
748 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
749 int forceMono = False;
751 if (appData.lowTimeWarning)
752 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
753 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
754 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
760 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
761 { // detervtomine what fonts to use, and create them
765 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
766 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
767 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
768 appData.font = fontTable[MESSAGE_FONT][squareSize];
769 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
770 appData.coordFont = fontTable[COORD_FONT][squareSize];
773 appData.font = InsertPxlSize(appData.font, fontPxlSize);
774 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
775 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
776 fontSet = CreateFontSet(appData.font);
777 clockFontSet = CreateFontSet(appData.clockFont);
779 /* For the coordFont, use the 0th font of the fontset. */
780 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
781 XFontStruct **font_struct_list;
782 XFontSetExtents *fontSize;
783 char **font_name_list;
784 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
785 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
786 coordFontStruct = XQueryFont(xDisplay, coordFontID);
787 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
788 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
791 appData.font = FindFont(appData.font, fontPxlSize);
792 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
793 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
794 clockFontID = XLoadFont(xDisplay, appData.clockFont);
795 clockFontStruct = XQueryFont(xDisplay, clockFontID);
796 coordFontID = XLoadFont(xDisplay, appData.coordFont);
797 coordFontStruct = XQueryFont(xDisplay, coordFontID);
798 // textHeight in !NLS mode!
800 countFontID = coordFontID; // [HGM] holdings
801 countFontStruct = coordFontStruct;
803 xdb = XtDatabase(xDisplay);
805 XrmPutLineResource(&xdb, "*international: True");
806 vTo.size = sizeof(XFontSet);
807 vTo.addr = (XtPointer) &fontSet;
808 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
810 XrmPutStringResource(&xdb, "*font", appData.font);
820 case ArgInt: p = " N"; break;
821 case ArgString: p = " STR"; break;
822 case ArgBoolean: p = " TF"; break;
823 case ArgSettingsFilename:
824 case ArgBackupSettingsFile:
825 case ArgFilename: p = " FILE"; break;
826 case ArgX: p = " Nx"; break;
827 case ArgY: p = " Ny"; break;
828 case ArgAttribs: p = " TEXTCOL"; break;
829 case ArgColor: p = " COL"; break;
830 case ArgFont: p = " FONT"; break;
831 case ArgBoardSize: p = " SIZE"; break;
832 case ArgFloat: p = " FLOAT"; break;
837 case ArgCommSettings:
846 GenerateGlobalTranslationTable (void)
848 /* go through all menu items and extract the keyboard shortcuts, so that X11 can load them */
854 output[0] = strdup(""); // build keystrokes with and wo mod keys separately
855 output[1] = strdup(""); // so the more specific can preceed the other
857 /* loop over all menu entries */
858 for( i=0; menuBar[i-n].mi || !n++; i++)
860 mi = menuBar[i+n].mi; // kludge to access 'noMenu' behind sentinel
861 for(j=0; mi[j].proc; j++)
869 char *key,*test, *mods;
871 /* check for Ctrl/Alt */
872 if( strstr(mi[j].accel, "<Ctrl>") ) ctrl = 1;
873 if( strstr(mi[j].accel, "<Shift>") ) shift = 1;
874 if( strstr(mi[j].accel, "<Alt>") ) alt = 1;
876 /* remove all <...> */
877 test = strrchr(mi[j].accel, '>');
879 key = strdup(mi[j].accel);
881 key = strdup(++test); // remove ">"
883 /* instead of shift X11 uses the uppercase letter directly*/
884 if (shift && strlen(key)==1 )
886 *key = toupper(*key);
890 /* handle some special cases which have different names in X11 */
891 if ( strncmp(key, "Page_Down", 9) == 0 )
896 else if ( strncmp(key, "Page_Up", 7) == 0 )
902 /* create string of mods */
904 mods = strdup("Ctrl ");
910 mods = realloc(mods, strlen(mods) + strlen("Meta ")+1);
911 strncat(mods, "Meta ", 5);
916 mods = realloc(mods, strlen(mods) + strlen("Shift ")+1);
917 strncat(mods, "Shift ", 6);
920 // remove trailing space
921 if( isspace(mods[strlen(mods)-1]) )
922 mods[strlen(mods)-1]='\0';
924 /* get the name for the callback, we can use MenuItem() here that will call KeyBindingProc */
925 char *name = malloc(MSG_SIZ);
926 if(n) snprintf(name, MSG_SIZ, "%s", mi[j].ref);
927 else snprintf(name, MSG_SIZ, "%s.%s", menuBar[i].ref, mi[j].ref);
929 char *buffer = malloc(MSG_SIZ);
930 snprintf(buffer, MSG_SIZ, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
932 /* add string to the output */
933 output[shift|alt|ctrl] = realloc(output[shift|alt|ctrl], strlen(output[shift|alt|ctrl]) + strlen(buffer)+1);
934 strncat(output[shift|alt|ctrl], buffer, strlen(buffer));
944 output[1] = realloc(output[1], strlen(output[1]) + strlen(output[0])+1);
945 strncat(output[1], output[0], strlen(output[0]));
956 ArgDescriptor *q, *p = argDescriptors+5;
957 printf("\nXBoard accepts the following options:\n"
958 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
959 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
960 " SIZE = board-size spec(s)\n"
961 " Within parentheses are short forms, or options to set to true or false.\n"
962 " Persistent options (saved in the settings file) are marked with *)\n\n");
964 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
965 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
966 if(p->save) strcat(buf+len, "*");
967 for(q=p+1; q->argLoc == p->argLoc; q++) {
968 if(q->argName[0] == '-') continue;
969 strcat(buf+len, q == p+1 ? " (" : " ");
970 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
972 if(q != p+1) strcat(buf+len, ")");
974 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
977 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
981 SlaveResize (Option *opt)
986 main (int argc, char **argv)
988 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
989 XSetWindowAttributes window_attributes;
991 Dimension boardWidth, boardHeight, w, h;
993 int forceMono = False;
995 extern Option chatOptions[]; // FIXME: adapt Chat window, removing ICS pane and Hide button
996 chatOptions[6].type = chatOptions[10].type = Skip;
998 srandom(time(0)); // [HGM] book: make random truly random
1000 setbuf(stdout, NULL);
1001 setbuf(stderr, NULL);
1004 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1005 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
1009 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1014 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
1015 typedef struct {char *name, *value; } Config;
1016 static Config configList[] = {
1017 { "Datadir", DATADIR },
1018 { "Sysconfdir", SYSCONFDIR },
1023 for(i=0; configList[i].name; i++) {
1024 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
1025 if(argc > 2) printf("%s", configList[i].value);
1026 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
1031 programName = strrchr(argv[0], '/');
1032 if (programName == NULL)
1033 programName = argv[0];
1038 XtSetLanguageProc(NULL, NULL, NULL);
1039 if (appData.debugMode) {
1040 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1043 bindtextdomain(PACKAGE, LOCALEDIR);
1044 textdomain(PACKAGE);
1047 appData.boardSize = "";
1048 InitAppData(ConvertToLine(argc, argv));
1050 if (p == NULL) p = "/tmp";
1051 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1052 gameCopyFilename = (char*) malloc(i);
1053 gamePasteFilename = (char*) malloc(i);
1054 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1055 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1057 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1058 static char buf[MSG_SIZ];
1059 EscapeExpand(buf, appData.firstInitString);
1060 appData.firstInitString = strdup(buf);
1061 EscapeExpand(buf, appData.secondInitString);
1062 appData.secondInitString = strdup(buf);
1063 EscapeExpand(buf, appData.firstComputerString);
1064 appData.firstComputerString = strdup(buf);
1065 EscapeExpand(buf, appData.secondComputerString);
1066 appData.secondComputerString = strdup(buf);
1069 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1072 if (chdir(chessDir) != 0) {
1073 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1079 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1080 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1081 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1082 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1085 setbuf(debugFP, NULL);
1088 /* [HGM,HR] make sure board size is acceptable */
1089 if(appData.NrFiles > BOARD_FILES ||
1090 appData.NrRanks > BOARD_RANKS )
1091 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1094 /* This feature does not work; animation needs a rewrite */
1095 appData.highlightDragging = FALSE;
1099 gameInfo.variant = StringToVariant(appData.variant);
1100 InitPosition(FALSE);
1103 XtAppInitialize(&appContext, "XBoard", shellOptions,
1104 XtNumber(shellOptions),
1105 &argc, argv, xboardResources, NULL, 0);
1107 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1108 clientResources, XtNumber(clientResources),
1111 xDisplay = XtDisplay(shellWidget);
1112 xScreen = DefaultScreen(xDisplay);
1113 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1116 * determine size, based on supplied or remembered -size, or screen size
1118 if (isdigit(appData.boardSize[0])) {
1119 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1120 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1121 &fontPxlSize, &smallLayout, &tinyLayout);
1123 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1124 programName, appData.boardSize);
1128 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // scale height
1130 /* Find some defaults; use the nearest known size */
1131 SizeDefaults *szd, *nearest;
1132 int distance = 99999;
1133 nearest = szd = sizeDefaults;
1134 while (szd->name != NULL) {
1135 if (abs(szd->squareSize - squareSize) < distance) {
1137 distance = abs(szd->squareSize - squareSize);
1138 if (distance == 0) break;
1142 if (i < 2) lineGap = nearest->lineGap;
1143 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1144 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1145 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1146 if (i < 6) smallLayout = nearest->smallLayout;
1147 if (i < 7) tinyLayout = nearest->tinyLayout;
1150 SizeDefaults *szd = sizeDefaults;
1151 if (*appData.boardSize == NULLCHAR) {
1152 while (DisplayWidth(xDisplay, xScreen) < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1153 DisplayHeight(xDisplay, xScreen) < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1156 if (szd->name == NULL) szd--;
1157 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1159 while (szd->name != NULL &&
1160 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1161 if (szd->name == NULL) {
1162 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1163 programName, appData.boardSize);
1167 squareSize = szd->squareSize;
1168 lineGap = szd->lineGap;
1169 clockFontPxlSize = szd->clockFontPxlSize;
1170 coordFontPxlSize = szd->coordFontPxlSize;
1171 fontPxlSize = szd->fontPxlSize;
1172 smallLayout = szd->smallLayout;
1173 tinyLayout = szd->tinyLayout;
1174 // [HGM] font: use defaults from settings file if available and not overruled
1177 defaultLineGap = lineGap;
1178 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1180 /* [HR] height treated separately (hacked) */
1181 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1182 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1185 * Determine what fonts to use.
1187 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1190 * Detect if there are not enough colors available and adapt.
1192 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1193 appData.monoMode = True;
1196 forceMono = MakeColors();
1199 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1201 appData.monoMode = True;
1204 if (appData.monoMode && appData.debugMode) {
1205 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1206 (unsigned long) XWhitePixel(xDisplay, xScreen),
1207 (unsigned long) XBlackPixel(xDisplay, xScreen));
1210 ParseIcsTextColors();
1212 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1218 layoutName = "tinyLayout";
1219 } else if (smallLayout) {
1220 layoutName = "smallLayout";
1222 layoutName = "normalLayout";
1225 optList = BoardPopUp(squareSize, lineGap, (void*)
1231 InitDrawingHandle(optList + W_BOARD);
1232 currBoard = &optList[W_BOARD];
1233 boardWidget = optList[W_BOARD].handle;
1234 menuBarWidget = optList[W_MENU].handle;
1235 dropMenu = optList[W_DROP].handle;
1236 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1237 formWidget = XtParent(boardWidget);
1238 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1239 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1240 XtGetValues(optList[W_WHITE].handle, args, 2);
1241 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1242 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1243 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1244 XtGetValues(optList[W_PAUSE].handle, args, 2);
1247 xBoardWindow = XtWindow(boardWidget);
1249 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1250 // not need to go into InitDrawingSizes().
1253 * Create X checkmark bitmap and initialize option menu checks.
1255 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1256 checkmark_bits, checkmark_width, checkmark_height);
1262 ReadBitmap(&wIconPixmap, "icon_white.bm",
1263 icon_white_bits, icon_white_width, icon_white_height);
1264 ReadBitmap(&bIconPixmap, "icon_black.bm",
1265 icon_black_bits, icon_black_width, icon_black_height);
1266 iconPixmap = wIconPixmap;
1268 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1269 XtSetValues(shellWidget, args, i);
1272 * Create a cursor for the board widget.
1274 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1275 XChangeWindowAttributes(xDisplay, xBoardWindow,
1276 CWCursor, &window_attributes);
1279 * Inhibit shell resizing.
1281 shellArgs[0].value = (XtArgVal) &w;
1282 shellArgs[1].value = (XtArgVal) &h;
1283 XtGetValues(shellWidget, shellArgs, 2);
1284 shellArgs[4].value = shellArgs[2].value = w;
1285 shellArgs[5].value = shellArgs[3].value = h;
1286 // XtSetValues(shellWidget, &shellArgs[2], 4);
1287 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1288 marginH = h - boardHeight;
1290 CatchDeleteWindow(shellWidget, "QuitProc");
1295 if(appData.logoSize)
1296 { // locate and read user logo
1297 char buf[MSG_SIZ], name[MSG_SIZ];
1298 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1299 if(!FindLogo(name, ".logo", buf))
1300 FindLogo(appData.logoDir, name + 6, buf);
1301 ASSIGN(userLogo, buf);
1304 if (appData.animate || appData.animateDragging)
1308 char *TranslationsTableMenus=GenerateGlobalTranslationTable ();
1310 XtAugmentTranslations(formWidget,
1311 XtParseTranslationTable(globalTranslations));
1312 XtAugmentTranslations(formWidget,
1313 XtParseTranslationTable(TranslationsTableMenus));
1315 XtAddEventHandler(formWidget, KeyPressMask, False,
1316 (XtEventHandler) MoveTypeInProc, NULL);
1317 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1318 (XtEventHandler) EventProc, NULL);
1320 /* [AS] Restore layout */
1321 if( wpMoveHistory.visible ) {
1325 if( wpEvalGraph.visible )
1330 if( wpEngineOutput.visible ) {
1331 EngineOutputPopUp();
1334 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1339 if (errorExitStatus == -1) {
1340 if (appData.icsActive) {
1341 /* We now wait until we see "login:" from the ICS before
1342 sending the logon script (problems with timestamp otherwise) */
1343 /*ICSInitScript();*/
1344 if (appData.icsInputBox) ICSInputBoxPopUp();
1348 signal(SIGWINCH, TermSizeSigHandler);
1350 signal(SIGINT, IntSigHandler);
1351 signal(SIGTERM, IntSigHandler);
1352 if (*appData.cmailGameName != NULLCHAR) {
1353 signal(SIGUSR1, CmailSigHandler);
1358 // XtSetKeyboardFocus(shellWidget, formWidget);
1359 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1361 XtAppMainLoop(appContext);
1362 if (appData.debugMode) fclose(debugFP); // [DM] debug
1370 while((m = XtAppPending(appContext))) XtAppProcessEvent(appContext, m);
1374 TermSizeSigHandler (int sig)
1380 IntSigHandler (int sig)
1386 CmailSigHandler (int sig)
1391 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1393 /* Activate call-back function CmailSigHandlerCallBack() */
1394 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1396 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1400 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1403 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1405 /**** end signal code ****/
1408 #define Abs(n) ((n)<0 ? -(n) : (n))
1412 InsertPxlSize (char *pattern, int targetPxlSize)
1414 char *base_fnt_lst, strInt[12], *p, *q;
1415 int alternatives, i, len, strIntLen;
1418 * Replace the "*" (if present) in the pixel-size slot of each
1419 * alternative with the targetPxlSize.
1423 while ((p = strchr(p, ',')) != NULL) {
1427 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1428 strIntLen = strlen(strInt);
1429 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1433 while (alternatives--) {
1434 char *comma = strchr(p, ',');
1435 for (i=0; i<14; i++) {
1436 char *hyphen = strchr(p, '-');
1438 if (comma && hyphen > comma) break;
1439 len = hyphen + 1 - p;
1440 if (i == 7 && *p == '*' && len == 2) {
1442 memcpy(q, strInt, strIntLen);
1452 len = comma + 1 - p;
1459 return base_fnt_lst;
1463 CreateFontSet (char *base_fnt_lst)
1466 char **missing_list;
1470 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1471 &missing_list, &missing_count, &def_string);
1472 if (appData.debugMode) {
1474 XFontStruct **font_struct_list;
1475 char **font_name_list;
1476 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1478 fprintf(debugFP, " got list %s, locale %s\n",
1479 XBaseFontNameListOfFontSet(fntSet),
1480 XLocaleOfFontSet(fntSet));
1481 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1482 for (i = 0; i < count; i++) {
1483 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1486 for (i = 0; i < missing_count; i++) {
1487 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1490 if (fntSet == NULL) {
1491 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1496 #else // not ENABLE_NLS
1498 * Find a font that matches "pattern" that is as close as
1499 * possible to the targetPxlSize. Prefer fonts that are k
1500 * pixels smaller to fonts that are k pixels larger. The
1501 * pattern must be in the X Consortium standard format,
1502 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1503 * The return value should be freed with XtFree when no
1507 FindFont (char *pattern, int targetPxlSize)
1509 char **fonts, *p, *best, *scalable, *scalableTail;
1510 int i, j, nfonts, minerr, err, pxlSize;
1512 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1514 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1515 programName, pattern);
1522 for (i=0; i<nfonts; i++) {
1525 if (*p != '-') continue;
1527 if (*p == NULLCHAR) break;
1528 if (*p++ == '-') j++;
1530 if (j < 7) continue;
1533 scalable = fonts[i];
1536 err = pxlSize - targetPxlSize;
1537 if (Abs(err) < Abs(minerr) ||
1538 (minerr > 0 && err < 0 && -err == minerr)) {
1544 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1545 /* If the error is too big and there is a scalable font,
1546 use the scalable font. */
1547 int headlen = scalableTail - scalable;
1548 p = (char *) XtMalloc(strlen(scalable) + 10);
1549 while (isdigit(*scalableTail)) scalableTail++;
1550 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1552 p = (char *) XtMalloc(strlen(best) + 2);
1553 safeStrCpy(p, best, strlen(best)+1 );
1555 if (appData.debugMode) {
1556 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1557 pattern, targetPxlSize, p);
1559 XFreeFontNames(fonts);
1565 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1568 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1574 MarkMenuItem (char *menuRef, int state)
1576 MenuItem *item = MenuNameToItem(menuRef);
1580 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1581 XtSetValues(item->handle, args, 1);
1586 EnableNamedMenuItem (char *menuRef, int state)
1588 MenuItem *item = MenuNameToItem(menuRef);
1590 if(item) XtSetSensitive(item->handle, state);
1594 EnableButtonBar (int state)
1596 XtSetSensitive(optList[W_BUTTON].handle, state);
1601 SetMenuEnables (Enables *enab)
1603 while (enab->name != NULL) {
1604 EnableNamedMenuItem(enab->name, enab->value);
1610 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1611 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1613 if(*nprms == 0) return;
1614 item = MenuNameToItem(prms[0]);
1615 if(item) ((MenuProc *) item->proc) ();
1627 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1628 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1629 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1630 dmEnables[i].piece);
1631 XtSetSensitive(entry, p != NULL || !appData.testLegality
1632 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1633 && !appData.icsActive));
1635 while (p && *p++ == dmEnables[i].piece) count++;
1636 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1638 XtSetArg(args[j], XtNlabel, label); j++;
1639 XtSetValues(entry, args, j);
1644 do_flash_delay (unsigned long msec)
1650 FlashDelay (int flash_delay)
1652 XSync(xDisplay, False);
1653 if(flash_delay) do_flash_delay(flash_delay);
1657 Fraction (int x, int start, int stop)
1659 double f = ((double) x - start)/(stop - start);
1660 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1664 static WindowPlacement wpNew;
1667 CoDrag (Widget sh, WindowPlacement *wp)
1670 int j=0, touch=0, fudge = 2;
1671 GetActualPlacement(sh, wp);
1672 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1673 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1674 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1675 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1676 if(!touch ) return; // only windows that touch co-move
1677 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1678 int heightInc = wpNew.height - wpMain.height;
1679 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1680 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1681 wp->y += fracTop * heightInc;
1682 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1683 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1684 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1685 int widthInc = wpNew.width - wpMain.width;
1686 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1687 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1688 wp->y += fracLeft * widthInc;
1689 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1690 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1692 wp->x += wpNew.x - wpMain.x;
1693 wp->y += wpNew.y - wpMain.y;
1694 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1695 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1696 XtSetArg(args[j], XtNx, wp->x); j++;
1697 XtSetArg(args[j], XtNy, wp->y); j++;
1698 XtSetValues(sh, args, j);
1702 ReSize (WindowPlacement *wp)
1705 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1706 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1707 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1708 if(sqy < sqx) sqx = sqy;
1709 if(sqx != squareSize) {
1710 squareSize = sqx; // adopt new square size
1711 CreatePNGPieces(); // make newly scaled pieces
1712 InitDrawingSizes(0, 0); // creates grid etc.
1713 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1714 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1715 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1716 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1717 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1720 static XtIntervalId delayedDragID = 0;
1729 GetActualPlacement(shellWidget, &wpNew);
1730 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1731 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1732 busy = 0; return; // false alarm
1735 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1736 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1737 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1738 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1740 DrawPosition(True, NULL);
1741 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1749 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1751 XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1755 EventProc (Widget widget, caddr_t unused, XEvent *event)
1757 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1758 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1762 * event handler for redrawing the board
1765 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1767 DrawPosition(True, NULL);
1772 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1773 { // [HGM] pv: walk PV
1774 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1777 extern int savedIndex; /* gross that this is global */
1780 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1783 XawTextPosition index, dummy;
1786 XawTextGetSelectionPos(w, &index, &dummy);
1787 XtSetArg(arg, XtNstring, &val);
1788 XtGetValues(w, &arg, 1);
1789 ReplaceComment(savedIndex, val);
1790 if(savedIndex != currentMove) ToNrEvent(savedIndex);
1791 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1795 /* Disable all user input other than deleting the window */
1796 static int frozen = 0;
1802 /* Grab by a widget that doesn't accept input */
1803 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1807 /* Undo a FreezeUI */
1811 if (!frozen) return;
1812 XtRemoveGrab(optList[W_MESSG].handle);
1820 static int oldPausing = FALSE;
1821 static GameMode oldmode = (GameMode) -1;
1824 if (!boardWidget || !XtIsRealized(boardWidget)) return;
1826 if (pausing != oldPausing) {
1827 oldPausing = pausing;
1828 MarkMenuItem("Mode.Pause", pausing);
1830 if (appData.showButtonBar) {
1831 /* Always toggle, don't set. Previous code messes up when
1832 invoked while the button is pressed, as releasing it
1833 toggles the state again. */
1836 XtSetArg(args[0], XtNbackground, &oldbg);
1837 XtSetArg(args[1], XtNforeground, &oldfg);
1838 XtGetValues(optList[W_PAUSE].handle,
1840 XtSetArg(args[0], XtNbackground, oldfg);
1841 XtSetArg(args[1], XtNforeground, oldbg);
1843 XtSetValues(optList[W_PAUSE].handle, args, 2);
1847 wname = ModeToWidgetName(oldmode);
1848 if (wname != NULL) {
1849 MarkMenuItem(wname, False);
1851 wname = ModeToWidgetName(gameMode);
1852 if (wname != NULL) {
1853 MarkMenuItem(wname, True);
1856 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1858 /* Maybe all the enables should be handled here, not just this one */
1859 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1861 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1866 * Button/menu procedures
1869 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1870 char *selected_fen_position=NULL;
1873 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1874 Atom *type_return, XtPointer *value_return,
1875 unsigned long *length_return, int *format_return)
1877 char *selection_tmp;
1879 // if (!selected_fen_position) return False; /* should never happen */
1880 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1881 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1882 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1885 if (f == NULL) return False;
1889 selection_tmp = XtMalloc(len + 1);
1890 count = fread(selection_tmp, 1, len, f);
1893 XtFree(selection_tmp);
1896 selection_tmp[len] = NULLCHAR;
1898 /* note: since no XtSelectionDoneProc was registered, Xt will
1899 * automatically call XtFree on the value returned. So have to
1900 * make a copy of it allocated with XtMalloc */
1901 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1902 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1905 *value_return=selection_tmp;
1906 *length_return=strlen(selection_tmp);
1907 *type_return=*target;
1908 *format_return = 8; /* bits per byte */
1910 } else if (*target == XA_TARGETS(xDisplay)) {
1911 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1912 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1913 targets_tmp[1] = XA_STRING;
1914 *value_return = targets_tmp;
1915 *type_return = XA_ATOM;
1918 // This code leads to a read of value_return out of bounds on 64-bit systems.
1919 // Other code which I have seen always sets *format_return to 32 independent of
1920 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1921 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1922 *format_return = 8 * sizeof(Atom);
1923 if (*format_return > 32) {
1924 *length_return *= *format_return / 32;
1925 *format_return = 32;
1928 *format_return = 32;
1936 /* note: when called from menu all parameters are NULL, so no clue what the
1937 * Widget which was clicked on was, or what the click event was
1940 CopySomething (char *src)
1942 selected_fen_position = src;
1944 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
1945 * have a notion of a position that is selected but not copied.
1946 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
1948 XtOwnSelection(menuBarWidget, XA_PRIMARY,
1950 SendPositionSelection,
1951 NULL/* lose_ownership_proc */ ,
1952 NULL/* transfer_done_proc */);
1953 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
1955 SendPositionSelection,
1956 NULL/* lose_ownership_proc */ ,
1957 NULL/* transfer_done_proc */);
1960 /* function called when the data to Paste is ready */
1962 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
1963 Atom *type, XtPointer value, unsigned long *len, int *format)
1966 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
1967 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
1968 EditPositionPasteFEN(fenstr);
1972 /* called when Paste Position button is pressed,
1973 * all parameters will be NULL */
1975 PastePositionProc ()
1977 XtGetSelectionValue(menuBarWidget,
1978 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
1979 /* (XtSelectionCallbackProc) */ PastePositionCB,
1980 NULL, /* client_data passed to PastePositionCB */
1982 /* better to use the time field from the event that triggered the
1983 * call to this function, but that isn't trivial to get
1990 /* note: when called from menu all parameters are NULL, so no clue what the
1991 * Widget which was clicked on was, or what the click event was
1993 /* function called when the data to Paste is ready */
1995 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
1996 Atom *type, XtPointer value, unsigned long *len, int *format)
1999 if (value == NULL || *len == 0) {
2000 return; /* nothing had been selected to copy */
2002 f = fopen(gamePasteFilename, "w");
2004 DisplayError(_("Can't open temp file"), errno);
2007 fwrite(value, 1, *len, f);
2010 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2013 /* called when Paste Game button is pressed,
2014 * all parameters will be NULL */
2018 XtGetSelectionValue(menuBarWidget,
2019 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2020 /* (XtSelectionCallbackProc) */ PasteGameCB,
2021 NULL, /* client_data passed to PasteGameCB */
2023 /* better to use the time field from the event that triggered the
2024 * call to this function, but that isn't trivial to get
2033 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2040 { // bassic primitive for determining if modifier keys are pressed
2041 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2044 XQueryKeymap(xDisplay,keys);
2045 for(i=0; i<6; i++) {
2047 j = XKeysymToKeycode(xDisplay, codes[i]);
2048 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2054 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2058 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2059 if ( n == 1 && *buf >= 32 // printable
2060 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2061 ) BoxAutoPopUp (buf);
2065 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2066 { // [HGM] input: let up-arrow recall previous line from history
2071 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2072 { // [HGM] input: let down-arrow recall next line from history
2077 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2083 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2085 if (!TempBackwardActive) {
2086 TempBackwardActive = True;
2092 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2094 /* Check to see if triggered by a key release event for a repeating key.
2095 * If so the next queued event will be a key press of the same key at the same time */
2096 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2098 XPeekEvent(xDisplay, &next);
2099 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2100 next.xkey.keycode == event->xkey.keycode)
2104 TempBackwardActive = False;
2108 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2109 { // called as key binding
2112 if (nprms && *nprms > 0)
2116 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2122 { // called from menu
2123 ManInner(NULL, NULL, NULL, NULL);
2130 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
2136 SetWindowTitle (char *text, char *title, char *icon)
2140 if (appData.titleInWindow) {
2142 XtSetArg(args[i], XtNlabel, text); i++;
2143 XtSetValues(titleWidget, args, i);
2146 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2147 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2148 XtSetValues(shellWidget, args, i);
2149 XSync(xDisplay, False);
2154 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2160 DisplayIcsInteractionTitle (String message)
2162 if (oldICSInteractionTitle == NULL) {
2163 /* Magic to find the old window title, adapted from vim */
2164 char *wina = getenv("WINDOWID");
2166 Window win = (Window) atoi(wina);
2167 Window root, parent, *children;
2168 unsigned int nchildren;
2169 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2171 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2172 if (!XQueryTree(xDisplay, win, &root, &parent,
2173 &children, &nchildren)) break;
2174 if (children) XFree((void *)children);
2175 if (parent == root || parent == 0) break;
2178 XSetErrorHandler(oldHandler);
2180 if (oldICSInteractionTitle == NULL) {
2181 oldICSInteractionTitle = "xterm";
2184 printf("\033]0;%s\007", message);
2189 XtIntervalId delayedEventTimerXID = 0;
2190 DelayedEventCallback delayedEventCallback = 0;
2195 delayedEventTimerXID = 0;
2196 delayedEventCallback();
2200 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2202 if(delayedEventTimerXID && delayedEventCallback == cb)
2203 // [HGM] alive: replace, rather than add or flush identical event
2204 XtRemoveTimeOut(delayedEventTimerXID);
2205 delayedEventCallback = cb;
2206 delayedEventTimerXID =
2207 XtAppAddTimeOut(appContext, millisec,
2208 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2211 DelayedEventCallback
2214 if (delayedEventTimerXID) {
2215 return delayedEventCallback;
2222 CancelDelayedEvent ()
2224 if (delayedEventTimerXID) {
2225 XtRemoveTimeOut(delayedEventTimerXID);
2226 delayedEventTimerXID = 0;
2230 XtIntervalId loadGameTimerXID = 0;
2233 LoadGameTimerRunning ()
2235 return loadGameTimerXID != 0;
2239 StopLoadGameTimer ()
2241 if (loadGameTimerXID != 0) {
2242 XtRemoveTimeOut(loadGameTimerXID);
2243 loadGameTimerXID = 0;
2251 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2253 loadGameTimerXID = 0;
2258 StartLoadGameTimer (long millisec)
2261 XtAppAddTimeOut(appContext, millisec,
2262 (XtTimerCallbackProc) LoadGameTimerCallback,
2266 XtIntervalId analysisClockXID = 0;
2269 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2271 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2272 || appData.icsEngineAnalyze) { // [DM]
2273 AnalysisPeriodicEvent(0);
2274 StartAnalysisClock();
2279 StartAnalysisClock ()
2282 XtAppAddTimeOut(appContext, 2000,
2283 (XtTimerCallbackProc) AnalysisClockCallback,
2287 XtIntervalId clockTimerXID = 0;
2290 ClockTimerRunning ()
2292 return clockTimerXID != 0;
2298 if (clockTimerXID != 0) {
2299 XtRemoveTimeOut(clockTimerXID);
2308 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2315 StartClockTimer (long millisec)
2318 XtAppAddTimeOut(appContext, millisec,
2319 (XtTimerCallbackProc) ClockTimerCallback,
2324 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2328 Widget w = (Widget) opt->handle;
2330 /* check for low time warning */
2331 Pixel foregroundOrWarningColor = timerForegroundPixel;
2334 appData.lowTimeWarning &&
2335 (timer / 1000) < appData.icsAlarmTime)
2336 foregroundOrWarningColor = lowTimeWarningColor;
2338 if (appData.clockMode) {
2339 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2340 XtSetArg(args[0], XtNlabel, buf);
2342 snprintf(buf, MSG_SIZ, "%s ", color);
2343 XtSetArg(args[0], XtNlabel, buf);
2348 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2349 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2351 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2352 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2355 XtSetValues(w, args, 3);
2358 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2361 SetClockIcon (int color)
2364 Pixmap pm = *clockIcons[color];
2365 if (iconPixmap != pm) {
2367 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2368 XtSetValues(shellWidget, args, 1);
2372 #define INPUT_SOURCE_BUF_SIZE 8192
2381 char buf[INPUT_SOURCE_BUF_SIZE];
2386 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2388 InputSource *is = (InputSource *) closure;
2393 if (is->lineByLine) {
2394 count = read(is->fd, is->unused,
2395 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2397 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2400 is->unused += count;
2402 while (p < is->unused) {
2403 q = memchr(p, '\n', is->unused - p);
2404 if (q == NULL) break;
2406 (is->func)(is, is->closure, p, q - p, 0);
2410 while (p < is->unused) {
2415 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2420 (is->func)(is, is->closure, is->buf, count, error);
2425 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2428 ChildProc *cp = (ChildProc *) pr;
2430 is = (InputSource *) calloc(1, sizeof(InputSource));
2431 is->lineByLine = lineByLine;
2435 is->fd = fileno(stdin);
2437 is->kind = cp->kind;
2438 is->fd = cp->fdFrom;
2441 is->unused = is->buf;
2444 is->xid = XtAppAddInput(appContext, is->fd,
2445 (XtPointer) (XtInputReadMask),
2446 (XtInputCallbackProc) DoInputCallback,
2448 is->closure = closure;
2449 return (InputSourceRef) is;
2453 RemoveInputSource (InputSourceRef isr)
2455 InputSource *is = (InputSource *) isr;
2457 if (is->xid == 0) return;
2458 XtRemoveInput(is->xid);
2464 static Boolean frameWaiting;
2467 FrameAlarm (int sig)
2469 frameWaiting = False;
2470 /* In case System-V style signals. Needed?? */
2471 signal(SIGALRM, FrameAlarm);
2475 FrameDelay (int time)
2477 struct itimerval delay;
2479 XSync(xDisplay, False);
2482 frameWaiting = True;
2483 signal(SIGALRM, FrameAlarm);
2484 delay.it_interval.tv_sec =
2485 delay.it_value.tv_sec = time / 1000;
2486 delay.it_interval.tv_usec =
2487 delay.it_value.tv_usec = (time % 1000) * 1000;
2488 setitimer(ITIMER_REAL, &delay, NULL);
2489 while (frameWaiting) pause();
2490 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2491 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2492 setitimer(ITIMER_REAL, &delay, NULL);
2499 FrameDelay (int time)
2501 XSync(xDisplay, False);
2503 usleep(time * 1000);
2509 FindLogo (char *place, char *name, char *buf)
2510 { // check if file exists in given place
2512 if(!place) return 0;
2513 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2514 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2522 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2524 char buf[MSG_SIZ], *logoName = buf;
2525 if(appData.logo[n][0]) {
2526 logoName = appData.logo[n];
2527 } else if(appData.autoLogo) {
2528 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2529 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2530 } else { // engine; cascade
2531 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2532 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2533 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2534 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2538 { ASSIGN(cps->programLogo, logoName); }
2542 UpdateLogos (int displ)
2544 if(optList[W_WHITE-1].handle == NULL) return;
2545 LoadLogo(&first, 0, 0);
2546 LoadLogo(&second, 1, appData.icsActive);
2547 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);