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 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>
68 # if HAVE_SYS_SOCKET_H
69 # include <sys/socket.h>
70 # include <netinet/in.h>
72 # else /* not HAVE_SYS_SOCKET_H */
73 # if HAVE_LAN_SOCKET_H
74 # include <lan/socket.h>
76 # include <lan/netdb.h>
77 # else /* not HAVE_LAN_SOCKET_H */
78 # define OMIT_SOCKETS 1
79 # endif /* not HAVE_LAN_SOCKET_H */
80 # endif /* not HAVE_SYS_SOCKET_H */
81 #endif /* !OMIT_SOCKETS */
86 #else /* not STDC_HEADERS */
87 extern char *getenv();
90 # else /* not HAVE_STRING_H */
92 # endif /* not HAVE_STRING_H */
93 #endif /* not STDC_HEADERS */
96 # include <sys/fcntl.h>
97 #else /* not HAVE_SYS_FCNTL_H */
100 # endif /* HAVE_FCNTL_H */
101 #endif /* not HAVE_SYS_FCNTL_H */
103 #if HAVE_SYS_SYSTEMINFO_H
104 # include <sys/systeminfo.h>
105 #endif /* HAVE_SYS_SYSTEMINFO_H */
107 #if TIME_WITH_SYS_TIME
108 # include <sys/time.h>
112 # include <sys/time.h>
123 # include <sys/wait.h>
128 # define NAMLEN(dirent) strlen((dirent)->d_name)
129 # define HAVE_DIR_STRUCT
131 # define dirent direct
132 # define NAMLEN(dirent) (dirent)->d_namlen
134 # include <sys/ndir.h>
135 # define HAVE_DIR_STRUCT
138 # include <sys/dir.h>
139 # define HAVE_DIR_STRUCT
143 # define HAVE_DIR_STRUCT
151 #include <X11/keysym.h>
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);
1855 if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
1856 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1859 /* Maybe all the enables should be handled here, not just this one */
1860 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1862 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1867 * Button/menu procedures
1870 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1871 char *selected_fen_position=NULL;
1874 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1875 Atom *type_return, XtPointer *value_return,
1876 unsigned long *length_return, int *format_return)
1878 char *selection_tmp;
1880 // if (!selected_fen_position) return False; /* should never happen */
1881 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1882 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1883 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1886 if (f == NULL) return False;
1890 selection_tmp = XtMalloc(len + 1);
1891 count = fread(selection_tmp, 1, len, f);
1894 XtFree(selection_tmp);
1897 selection_tmp[len] = NULLCHAR;
1899 /* note: since no XtSelectionDoneProc was registered, Xt will
1900 * automatically call XtFree on the value returned. So have to
1901 * make a copy of it allocated with XtMalloc */
1902 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1903 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1906 *value_return=selection_tmp;
1907 *length_return=strlen(selection_tmp);
1908 *type_return=*target;
1909 *format_return = 8; /* bits per byte */
1911 } else if (*target == XA_TARGETS(xDisplay)) {
1912 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1913 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1914 targets_tmp[1] = XA_STRING;
1915 *value_return = targets_tmp;
1916 *type_return = XA_ATOM;
1919 // This code leads to a read of value_return out of bounds on 64-bit systems.
1920 // Other code which I have seen always sets *format_return to 32 independent of
1921 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1922 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1923 *format_return = 8 * sizeof(Atom);
1924 if (*format_return > 32) {
1925 *length_return *= *format_return / 32;
1926 *format_return = 32;
1929 *format_return = 32;
1937 /* note: when called from menu all parameters are NULL, so no clue what the
1938 * Widget which was clicked on was, or what the click event was
1941 CopySomething (char *src)
1943 selected_fen_position = src;
1945 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
1946 * have a notion of a position that is selected but not copied.
1947 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
1949 XtOwnSelection(menuBarWidget, XA_PRIMARY,
1951 SendPositionSelection,
1952 NULL/* lose_ownership_proc */ ,
1953 NULL/* transfer_done_proc */);
1954 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
1956 SendPositionSelection,
1957 NULL/* lose_ownership_proc */ ,
1958 NULL/* transfer_done_proc */);
1961 /* function called when the data to Paste is ready */
1963 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
1964 Atom *type, XtPointer value, unsigned long *len, int *format)
1967 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
1968 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
1969 EditPositionPasteFEN(fenstr);
1973 /* called when Paste Position button is pressed,
1974 * all parameters will be NULL */
1976 PastePositionProc ()
1978 XtGetSelectionValue(menuBarWidget,
1979 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
1980 /* (XtSelectionCallbackProc) */ PastePositionCB,
1981 NULL, /* client_data passed to PastePositionCB */
1983 /* better to use the time field from the event that triggered the
1984 * call to this function, but that isn't trivial to get
1991 /* note: when called from menu all parameters are NULL, so no clue what the
1992 * Widget which was clicked on was, or what the click event was
1994 /* function called when the data to Paste is ready */
1996 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
1997 Atom *type, XtPointer value, unsigned long *len, int *format)
2000 if (value == NULL || *len == 0) {
2001 return; /* nothing had been selected to copy */
2003 f = fopen(gamePasteFilename, "w");
2005 DisplayError(_("Can't open temp file"), errno);
2008 fwrite(value, 1, *len, f);
2011 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2014 /* called when Paste Game button is pressed,
2015 * all parameters will be NULL */
2019 XtGetSelectionValue(menuBarWidget,
2020 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2021 /* (XtSelectionCallbackProc) */ PasteGameCB,
2022 NULL, /* client_data passed to PasteGameCB */
2024 /* better to use the time field from the event that triggered the
2025 * call to this function, but that isn't trivial to get
2034 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2041 { // bassic primitive for determining if modifier keys are pressed
2042 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2045 XQueryKeymap(xDisplay,keys);
2046 for(i=0; i<6; i++) {
2048 j = XKeysymToKeycode(xDisplay, codes[i]);
2049 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2055 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2059 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2060 if ( n == 1 && *buf >= 32 // printable
2061 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2062 ) BoxAutoPopUp (buf);
2066 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2067 { // [HGM] input: let up-arrow recall previous line from history
2072 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2073 { // [HGM] input: let down-arrow recall next line from history
2078 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2084 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2086 if (!TempBackwardActive) {
2087 TempBackwardActive = True;
2093 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2095 /* Check to see if triggered by a key release event for a repeating key.
2096 * If so the next queued event will be a key press of the same key at the same time */
2097 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2099 XPeekEvent(xDisplay, &next);
2100 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2101 next.xkey.keycode == event->xkey.keycode)
2105 TempBackwardActive = False;
2109 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2110 { // called as key binding
2113 if (nprms && *nprms > 0)
2117 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2123 { // called from menu
2124 ManInner(NULL, NULL, NULL, NULL);
2131 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
2137 SetWindowTitle (char *text, char *title, char *icon)
2141 if (appData.titleInWindow) {
2143 XtSetArg(args[i], XtNlabel, text); i++;
2144 XtSetValues(titleWidget, args, i);
2147 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2148 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2149 XtSetValues(shellWidget, args, i);
2150 XSync(xDisplay, False);
2155 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2161 DisplayIcsInteractionTitle (String message)
2163 if (oldICSInteractionTitle == NULL) {
2164 /* Magic to find the old window title, adapted from vim */
2165 char *wina = getenv("WINDOWID");
2167 Window win = (Window) atoi(wina);
2168 Window root, parent, *children;
2169 unsigned int nchildren;
2170 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2172 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2173 if (!XQueryTree(xDisplay, win, &root, &parent,
2174 &children, &nchildren)) break;
2175 if (children) XFree((void *)children);
2176 if (parent == root || parent == 0) break;
2179 XSetErrorHandler(oldHandler);
2181 if (oldICSInteractionTitle == NULL) {
2182 oldICSInteractionTitle = "xterm";
2185 printf("\033]0;%s\007", message);
2190 XtIntervalId delayedEventTimerXID = 0;
2191 DelayedEventCallback delayedEventCallback = 0;
2196 delayedEventTimerXID = 0;
2197 delayedEventCallback();
2201 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2203 if(delayedEventTimerXID && delayedEventCallback == cb)
2204 // [HGM] alive: replace, rather than add or flush identical event
2205 XtRemoveTimeOut(delayedEventTimerXID);
2206 delayedEventCallback = cb;
2207 delayedEventTimerXID =
2208 XtAppAddTimeOut(appContext, millisec,
2209 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2212 DelayedEventCallback
2215 if (delayedEventTimerXID) {
2216 return delayedEventCallback;
2223 CancelDelayedEvent ()
2225 if (delayedEventTimerXID) {
2226 XtRemoveTimeOut(delayedEventTimerXID);
2227 delayedEventTimerXID = 0;
2231 XtIntervalId loadGameTimerXID = 0;
2234 LoadGameTimerRunning ()
2236 return loadGameTimerXID != 0;
2240 StopLoadGameTimer ()
2242 if (loadGameTimerXID != 0) {
2243 XtRemoveTimeOut(loadGameTimerXID);
2244 loadGameTimerXID = 0;
2252 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2254 loadGameTimerXID = 0;
2259 StartLoadGameTimer (long millisec)
2262 XtAppAddTimeOut(appContext, millisec,
2263 (XtTimerCallbackProc) LoadGameTimerCallback,
2267 XtIntervalId analysisClockXID = 0;
2270 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2272 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2273 || appData.icsEngineAnalyze) { // [DM]
2274 AnalysisPeriodicEvent(0);
2275 StartAnalysisClock();
2280 StartAnalysisClock ()
2283 XtAppAddTimeOut(appContext, 2000,
2284 (XtTimerCallbackProc) AnalysisClockCallback,
2288 XtIntervalId clockTimerXID = 0;
2291 ClockTimerRunning ()
2293 return clockTimerXID != 0;
2299 if (clockTimerXID != 0) {
2300 XtRemoveTimeOut(clockTimerXID);
2309 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2316 StartClockTimer (long millisec)
2319 XtAppAddTimeOut(appContext, millisec,
2320 (XtTimerCallbackProc) ClockTimerCallback,
2325 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2329 Widget w = (Widget) opt->handle;
2331 /* check for low time warning */
2332 Pixel foregroundOrWarningColor = timerForegroundPixel;
2335 appData.lowTimeWarning &&
2336 (timer / 1000) < appData.icsAlarmTime)
2337 foregroundOrWarningColor = lowTimeWarningColor;
2339 if (appData.clockMode) {
2340 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2341 XtSetArg(args[0], XtNlabel, buf);
2343 snprintf(buf, MSG_SIZ, "%s ", color);
2344 XtSetArg(args[0], XtNlabel, buf);
2349 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2350 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2352 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2353 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2356 XtSetValues(w, args, 3);
2359 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2362 SetClockIcon (int color)
2365 Pixmap pm = *clockIcons[color];
2366 if (iconPixmap != pm) {
2368 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2369 XtSetValues(shellWidget, args, 1);
2373 #define INPUT_SOURCE_BUF_SIZE 8192
2382 char buf[INPUT_SOURCE_BUF_SIZE];
2387 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2389 InputSource *is = (InputSource *) closure;
2394 if (is->lineByLine) {
2395 count = read(is->fd, is->unused,
2396 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2398 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2401 is->unused += count;
2403 while (p < is->unused) {
2404 q = memchr(p, '\n', is->unused - p);
2405 if (q == NULL) break;
2407 (is->func)(is, is->closure, p, q - p, 0);
2411 while (p < is->unused) {
2416 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2421 (is->func)(is, is->closure, is->buf, count, error);
2426 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2429 ChildProc *cp = (ChildProc *) pr;
2431 is = (InputSource *) calloc(1, sizeof(InputSource));
2432 is->lineByLine = lineByLine;
2436 is->fd = fileno(stdin);
2438 is->kind = cp->kind;
2439 is->fd = cp->fdFrom;
2442 is->unused = is->buf;
2445 is->xid = XtAppAddInput(appContext, is->fd,
2446 (XtPointer) (XtInputReadMask),
2447 (XtInputCallbackProc) DoInputCallback,
2449 is->closure = closure;
2450 return (InputSourceRef) is;
2454 RemoveInputSource (InputSourceRef isr)
2456 InputSource *is = (InputSource *) isr;
2458 if (is->xid == 0) return;
2459 XtRemoveInput(is->xid);
2465 static Boolean frameWaiting;
2468 FrameAlarm (int sig)
2470 frameWaiting = False;
2471 /* In case System-V style signals. Needed?? */
2472 signal(SIGALRM, FrameAlarm);
2476 FrameDelay (int time)
2478 struct itimerval delay;
2480 XSync(xDisplay, False);
2483 frameWaiting = True;
2484 signal(SIGALRM, FrameAlarm);
2485 delay.it_interval.tv_sec =
2486 delay.it_value.tv_sec = time / 1000;
2487 delay.it_interval.tv_usec =
2488 delay.it_value.tv_usec = (time % 1000) * 1000;
2489 setitimer(ITIMER_REAL, &delay, NULL);
2490 while (frameWaiting) pause();
2491 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2492 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2493 setitimer(ITIMER_REAL, &delay, NULL);
2500 FrameDelay (int time)
2502 XSync(xDisplay, False);
2504 usleep(time * 1000);
2510 FindLogo (char *place, char *name, char *buf)
2511 { // check if file exists in given place
2513 if(!place) return 0;
2514 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2515 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2523 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2525 char buf[MSG_SIZ], *logoName = buf;
2526 if(appData.logo[n][0]) {
2527 logoName = appData.logo[n];
2528 } else if(appData.autoLogo) {
2529 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2530 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2531 } else { // engine; cascade
2532 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2533 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2534 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2535 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2539 { ASSIGN(cps->programLogo, logoName); }
2543 UpdateLogos (int displ)
2545 if(optList[W_WHITE-1].handle == NULL) return;
2546 LoadLogo(&first, 0, 0);
2547 LoadLogo(&second, 1, appData.icsActive);
2548 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);