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/Intrinsic.h>
152 #include <X11/StringDefs.h>
153 #include <X11/Shell.h>
154 #include <X11/cursorfont.h>
155 #include <X11/Xatom.h>
156 #include <X11/Xmu/Atoms.h>
158 #include <X11/Xaw3d/Dialog.h>
159 #include <X11/Xaw3d/Form.h>
160 #include <X11/Xaw3d/List.h>
161 #include <X11/Xaw3d/Label.h>
162 #include <X11/Xaw3d/SimpleMenu.h>
163 #include <X11/Xaw3d/SmeBSB.h>
164 #include <X11/Xaw3d/SmeLine.h>
165 #include <X11/Xaw3d/Box.h>
166 #include <X11/Xaw3d/MenuButton.h>
167 #include <X11/Xaw3d/Text.h>
168 #include <X11/Xaw3d/AsciiText.h>
170 #include <X11/Xaw/Dialog.h>
171 #include <X11/Xaw/Form.h>
172 #include <X11/Xaw/List.h>
173 #include <X11/Xaw/Label.h>
174 #include <X11/Xaw/SimpleMenu.h>
175 #include <X11/Xaw/SmeBSB.h>
176 #include <X11/Xaw/SmeLine.h>
177 #include <X11/Xaw/Box.h>
178 #include <X11/Xaw/MenuButton.h>
179 #include <X11/Xaw/Text.h>
180 #include <X11/Xaw/AsciiText.h>
183 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "bitmaps/icon_white.bm"
187 #include "bitmaps/icon_black.bm"
188 #include "bitmaps/checkmark.bm"
190 #include "frontend.h"
192 #include "backendz.h"
196 #include "xgamelist.h"
197 #include "xhistory.h"
201 #include "engineoutput.h"
212 #define usleep(t) _sleep2(((t)+500)/1000)
216 # define _(s) gettext (s)
217 # define N_(s) gettext_noop (s)
223 int main P((int argc, char **argv));
224 RETSIGTYPE CmailSigHandler P((int sig));
225 RETSIGTYPE IntSigHandler P((int sig));
226 RETSIGTYPE TermSizeSigHandler P((int sig));
227 Widget CreateMenuBar P((Menu *mb, int boardWidth));
229 char *InsertPxlSize P((char *pattern, int targetPxlSize));
230 XFontSet CreateFontSet P((char *base_fnt_lst));
232 char *FindFont P((char *pattern, int targetPxlSize));
234 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
235 u_int wreq, u_int hreq));
236 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
237 void DelayedDrag P((void));
238 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
239 void HandlePV P((Widget w, XEvent * event,
240 String * params, Cardinal * nParams));
241 void DrawPositionProc P((Widget w, XEvent *event,
242 String *prms, Cardinal *nprms));
243 void CommentClick P((Widget w, XEvent * event,
244 String * params, Cardinal * nParams));
245 void ICSInputBoxPopUp P((void));
246 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
247 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
248 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
249 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
250 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
251 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
252 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
253 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
254 Boolean TempBackwardActive = False;
255 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
256 void DisplayMove P((int moveNumber));
257 void update_ics_width P(());
258 int CopyMemoProc P(());
259 static int FindLogo P((char *place, char *name, char *buf));
262 * XBoard depends on Xt R4 or higher
264 int xtVersion = XtSpecificationRelease;
269 Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
270 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
271 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
272 Option *optList; // contains all widgets of main window
274 XFontSet fontSet, clockFontSet;
277 XFontStruct *clockFontStruct;
279 Font coordFontID, countFontID;
280 XFontStruct *coordFontStruct, *countFontStruct;
281 XtAppContext appContext;
284 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
286 Position commentX = -1, commentY = -1;
287 Dimension commentW, commentH;
288 typedef unsigned int BoardSize;
290 Boolean chessProgram;
292 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
293 int smallLayout = 0, tinyLayout = 0,
294 marginW, marginH, // [HGM] for run-time resizing
295 fromX = -1, fromY = -1, toX, toY, commentUp = False,
296 errorExitStatus = -1, defaultLineGap;
297 Dimension textHeight;
298 Pixel timerForegroundPixel, timerBackgroundPixel;
299 Pixel buttonForegroundPixel, buttonBackgroundPixel;
300 char *chessDir, *programName, *programVersion;
301 Boolean alwaysOnTop = False;
302 char *icsTextMenuString;
304 char *firstChessProgramNames;
305 char *secondChessProgramNames;
307 WindowPlacement wpMain;
308 WindowPlacement wpConsole;
309 WindowPlacement wpComment;
310 WindowPlacement wpMoveHistory;
311 WindowPlacement wpEvalGraph;
312 WindowPlacement wpEngineOutput;
313 WindowPlacement wpGameList;
314 WindowPlacement wpTags;
315 WindowPlacement wpDualBoard;
318 /* This magic number is the number of intermediate frames used
319 in each half of the animation. For short moves it's reduced
320 by 1. The total number of frames will be factor * 2 + 1. */
323 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
330 DropMenuEnables dmEnables[] = {
347 XtResource clientResources[] = {
348 { "flashCount", "flashCount", XtRInt, sizeof(int),
349 XtOffset(AppDataPtr, flashCount), XtRImmediate,
350 (XtPointer) FLASH_COUNT },
353 XrmOptionDescRec shellOptions[] = {
354 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
355 { "-flash", "flashCount", XrmoptionNoArg, "3" },
356 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
359 XtActionsRec boardActions[] = {
360 { "DrawPosition", DrawPositionProc },
361 { "HandlePV", HandlePV },
362 { "SelectPV", SelectPV },
363 { "StopPV", StopPV },
364 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
365 { "QuitProc", QuitWrapper },
366 { "ManProc", ManInner },
367 { "TempBackwardProc", TempBackwardProc },
368 { "TempForwardProc", TempForwardProc },
369 { "CommentClick", (XtActionProc) CommentClick },
370 { "GenericPopDown", (XtActionProc) GenericPopDown },
371 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
372 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
373 { "SelectMove", (XtActionProc) SelectMoveX },
374 { "LoadSelectedProc", LoadSelectedProc },
375 { "SetFilterProc", SetFilterProc },
376 { "TypeInProc", TypeInProc },
377 { "EnterKeyProc", EnterKeyProc },
378 { "UpKeyProc", UpKeyProc },
379 { "DownKeyProc", DownKeyProc },
380 { "WheelProc", WheelProc },
381 { "TabProc", TabProc },
384 char globalTranslations[] =
385 ":Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
386 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
387 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
388 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
389 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
390 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
391 :<Key>Pause: MenuItem(Mode.Pause) \n \
392 :Ctrl<Key>d: MenuItem(DebugProc) \n \
393 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
394 :<Key>Left: MenuItem(Edit.Backward) \n \
395 :<Key>Right: MenuItem(Edit.Forward) \n \
396 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
397 #ifndef OPTIONSDIALOG
399 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
400 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
401 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
402 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
403 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
406 :<KeyDown>Return: TempBackwardProc() \n \
407 :<KeyUp>Return: TempForwardProc() \n";
409 char ICSInputTranslations[] =
410 "<Key>Up: UpKeyProc() \n "
411 "<Key>Down: DownKeyProc() \n "
412 "<Key>Return: EnterKeyProc() \n";
414 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
415 // as the widget is destroyed before the up-click can call extend-end
416 char commentTranslations[] = "<Btn3Down>: extend-end(PRIMARY) select-start() CommentClick() \n";
418 String xboardResources[] = {
419 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
424 /* Max possible square size */
425 #define MAXSQSIZE 256
427 /* Arrange to catch delete-window events */
428 Atom wm_delete_window;
430 CatchDeleteWindow (Widget w, String procname)
433 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
434 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
435 XtAugmentTranslations(w, XtParseTranslationTable(buf));
442 XtSetArg(args[0], XtNiconic, False);
443 XtSetValues(shellWidget, args, 1);
445 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
448 //---------------------------------------------------------------------------------------------------------
449 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
452 #define CW_USEDEFAULT (1<<31)
453 #define ICS_TEXT_MENU_SIZE 90
454 #define DEBUG_FILE "xboard.debug"
455 #define SetCurrentDirectory chdir
456 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
460 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
463 // front-end part of option handling
465 // [HGM] This platform-dependent table provides the location for storing the color info
466 extern char *crWhite, * crBlack;
470 &appData.whitePieceColor,
471 &appData.blackPieceColor,
472 &appData.lightSquareColor,
473 &appData.darkSquareColor,
474 &appData.highlightSquareColor,
475 &appData.premoveHighlightColor,
476 &appData.lowTimeWarningColor,
487 // [HGM] font: keep a font for each square size, even non-stndard ones
490 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
491 char *fontTable[NUM_FONTS][MAX_SIZE];
494 ParseFont (char *name, int number)
495 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
497 if(sscanf(name, "size%d:", &size)) {
498 // [HGM] font: font is meant for specific boardSize (likely from settings file);
499 // defer processing it until we know if it matches our board size
500 if(strstr(name, "-*-") && // only pay attention to things that look like X-fonts
501 size >= 0 && size<MAX_SIZE) { // for now, fixed limit
502 fontTable[number][size] = strdup(strchr(name, ':')+1);
503 fontValid[number][size] = True;
508 case 0: // CLOCK_FONT
509 appData.clockFont = strdup(name);
511 case 1: // MESSAGE_FONT
512 appData.font = strdup(name);
514 case 2: // COORD_FONT
515 appData.coordFont = strdup(name);
520 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
525 { // only 2 fonts currently
526 appData.clockFont = CLOCK_FONT_NAME;
527 appData.coordFont = COORD_FONT_NAME;
528 appData.font = DEFAULT_FONT_NAME;
533 { // no-op, until we identify the code for this already in XBoard and move it here
537 ParseColor (int n, char *name)
538 { // in XBoard, just copy the color-name string
539 if(colorVariable[n] && *name == '#') *(char**)colorVariable[n] = strdup(name);
545 return *(char**)colorVariable[n];
549 ParseTextAttribs (ColorClass cc, char *s)
551 (&appData.colorShout)[cc] = strdup(s);
555 ParseBoardSize (void *addr, char *name)
557 appData.boardSize = strdup(name);
562 { // In XBoard the sound-playing program takes care of obtaining the actual sound
566 SetCommPortDefaults ()
567 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
570 // [HGM] args: these three cases taken out to stay in front-end
572 SaveFontArg (FILE *f, ArgDescriptor *ad)
575 int i, n = (int)(intptr_t)ad->argLoc;
577 case 0: // CLOCK_FONT
578 name = appData.clockFont;
580 case 1: // MESSAGE_FONT
583 case 2: // COORD_FONT
584 name = appData.coordFont;
589 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
590 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
591 fontTable[n][squareSize] = strdup(name);
592 fontValid[n][squareSize] = True;
595 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
596 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
601 { // nothing to do, as the sounds are at all times represented by their text-string names already
605 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
606 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
607 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
611 SaveColor (FILE *f, ArgDescriptor *ad)
612 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
613 if(colorVariable[(int)(intptr_t)ad->argLoc])
614 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
618 SaveBoardSize (FILE *f, char *name, void *addr)
619 { // wrapper to shield back-end from BoardSize & sizeInfo
620 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
624 ParseCommPortSettings (char *s)
625 { // no such option in XBoard (yet)
631 GetActualPlacement (Widget wg, WindowPlacement *wp)
633 XWindowAttributes winAt;
640 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
641 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
642 wp->x = rx - winAt.x;
643 wp->y = ry - winAt.y;
644 wp->height = winAt.height;
645 wp->width = winAt.width;
646 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
650 GetPlacement (DialogClass dlg, WindowPlacement *wp)
651 { // wrapper to shield back-end from widget type
652 if(shellUp[dlg]) GetActualPlacement(shells[dlg], wp);
657 { // wrapper to shield use of window handles from back-end (make addressible by number?)
658 // In XBoard this will have to wait until awareness of window parameters is implemented
659 GetActualPlacement(shellWidget, &wpMain);
660 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
661 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
662 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
663 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
664 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
665 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
669 PrintCommPortSettings (FILE *f, char *name)
670 { // This option does not exist in XBoard
674 EnsureOnScreen (int *x, int *y, int minX, int minY)
681 { // [HGM] args: allows testing if main window is realized from back-end
682 return xBoardWindow != 0;
686 PopUpStartupDialog ()
687 { // start menu not implemented in XBoard
691 ConvertToLine (int argc, char **argv)
693 static char line[128*1024], buf[1024];
697 for(i=1; i<argc; i++)
699 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
700 && argv[i][0] != '{' )
701 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
703 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
704 strncat(line, buf, 128*1024 - strlen(line) - 1 );
707 line[strlen(line)-1] = NULLCHAR;
711 //--------------------------------------------------------------------------------------------
714 ResizeBoardWindow (int w, int h, int inhibit)
716 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
718 shellArgs[0].value = w;
719 shellArgs[1].value = h;
720 shellArgs[4].value = shellArgs[2].value = w;
721 shellArgs[5].value = shellArgs[3].value = h;
722 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
724 XSync(xDisplay, False);
728 MakeOneColor (char *name, Pixel *color)
731 if (!appData.monoMode) {
732 vFrom.addr = (caddr_t) name;
733 vFrom.size = strlen(name);
734 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
735 if (vTo.addr == NULL) {
736 appData.monoMode = True;
739 *color = *(Pixel *) vTo.addr;
747 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
748 int forceMono = False;
750 if (appData.lowTimeWarning)
751 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
752 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
753 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
759 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
760 { // detervtomine what fonts to use, and create them
764 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
765 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
766 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
767 appData.font = fontTable[MESSAGE_FONT][squareSize];
768 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
769 appData.coordFont = fontTable[COORD_FONT][squareSize];
772 appData.font = InsertPxlSize(appData.font, fontPxlSize);
773 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
774 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
775 fontSet = CreateFontSet(appData.font);
776 clockFontSet = CreateFontSet(appData.clockFont);
778 /* For the coordFont, use the 0th font of the fontset. */
779 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
780 XFontStruct **font_struct_list;
781 XFontSetExtents *fontSize;
782 char **font_name_list;
783 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
784 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
785 coordFontStruct = XQueryFont(xDisplay, coordFontID);
786 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
787 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
790 appData.font = FindFont(appData.font, fontPxlSize);
791 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
792 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
793 clockFontID = XLoadFont(xDisplay, appData.clockFont);
794 clockFontStruct = XQueryFont(xDisplay, clockFontID);
795 coordFontID = XLoadFont(xDisplay, appData.coordFont);
796 coordFontStruct = XQueryFont(xDisplay, coordFontID);
797 // textHeight in !NLS mode!
799 countFontID = coordFontID; // [HGM] holdings
800 countFontStruct = coordFontStruct;
802 xdb = XtDatabase(xDisplay);
804 XrmPutLineResource(&xdb, "*international: True");
805 vTo.size = sizeof(XFontSet);
806 vTo.addr = (XtPointer) &fontSet;
807 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
809 XrmPutStringResource(&xdb, "*font", appData.font);
819 case ArgInt: p = " N"; break;
820 case ArgString: p = " STR"; break;
821 case ArgBoolean: p = " TF"; break;
822 case ArgSettingsFilename:
823 case ArgBackupSettingsFile:
824 case ArgFilename: p = " FILE"; break;
825 case ArgX: p = " Nx"; break;
826 case ArgY: p = " Ny"; break;
827 case ArgAttribs: p = " TEXTCOL"; break;
828 case ArgColor: p = " COL"; break;
829 case ArgFont: p = " FONT"; break;
830 case ArgBoardSize: p = " SIZE"; break;
831 case ArgFloat: p = " FLOAT"; break;
836 case ArgCommSettings:
845 GenerateGlobalTranslationTable (void)
847 /* go through all menu items and extract the keyboard shortcuts, so that X11 can load them */
853 output[0] = strdup(""); // build keystrokes with and wo mod keys separately
854 output[1] = strdup(""); // so the more specific can preceed the other
856 /* loop over all menu entries */
857 for( i=0; menuBar[i-n].mi || !n++; i++)
859 mi = menuBar[i+n].mi; // kludge to access 'noMenu' behind sentinel
860 for(j=0; mi[j].proc; j++)
868 char *key,*test, *mods;
870 /* check for Ctrl/Alt */
871 if( strstr(mi[j].accel, "<Ctrl>") ) ctrl = 1;
872 if( strstr(mi[j].accel, "<Shift>") ) shift = 1;
873 if( strstr(mi[j].accel, "<Alt>") ) alt = 1;
875 /* remove all <...> */
876 test = strrchr(mi[j].accel, '>');
878 key = strdup(mi[j].accel);
880 key = strdup(++test); // remove ">"
882 /* instead of shift X11 uses the uppercase letter directly*/
883 if (shift && strlen(key)==1 )
885 *key = toupper(*key);
889 /* handle some special cases which have different names in X11 */
890 if ( strncmp(key, "Page_Down", 9) == 0 )
895 else if ( strncmp(key, "Page_Up", 7) == 0 )
901 /* create string of mods */
903 mods = strdup("Ctrl ");
909 mods = realloc(mods, strlen(mods) + strlen("Meta ")+1);
910 strncat(mods, "Meta ", 5);
915 mods = realloc(mods, strlen(mods) + strlen("Shift ")+1);
916 strncat(mods, "Shift ", 6);
919 // remove trailing space
920 if( isspace(mods[strlen(mods)-1]) )
921 mods[strlen(mods)-1]='\0';
923 /* get the name for the callback, we can use MenuItem() here that will call KeyBindingProc */
924 char *name = malloc(MSG_SIZ);
925 if(n) snprintf(name, MSG_SIZ, "%s", mi[j].ref);
926 else snprintf(name, MSG_SIZ, "%s.%s", menuBar[i].ref, mi[j].ref);
928 char *buffer = malloc(MSG_SIZ);
929 snprintf(buffer, MSG_SIZ, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
931 /* add string to the output */
932 output[shift|alt|ctrl] = realloc(output[shift|alt|ctrl], strlen(output[shift|alt|ctrl]) + strlen(buffer)+1);
933 strncat(output[shift|alt|ctrl], buffer, strlen(buffer));
943 output[1] = realloc(output[1], strlen(output[1]) + strlen(output[0])+1);
944 strncat(output[1], output[0], strlen(output[0]));
955 ArgDescriptor *q, *p = argDescriptors+5;
956 printf("\nXBoard accepts the following options:\n"
957 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
958 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
959 " SIZE = board-size spec(s)\n"
960 " Within parentheses are short forms, or options to set to true or false.\n"
961 " Persistent options (saved in the settings file) are marked with *)\n\n");
963 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
964 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
965 if(p->save) strcat(buf+len, "*");
966 for(q=p+1; q->argLoc == p->argLoc; q++) {
967 if(q->argName[0] == '-') continue;
968 strcat(buf+len, q == p+1 ? " (" : " ");
969 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
971 if(q != p+1) strcat(buf+len, ")");
973 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
976 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
980 SlaveResize (Option *opt)
985 main (int argc, char **argv)
987 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
988 XSetWindowAttributes window_attributes;
990 Dimension boardWidth, boardHeight, w, h;
992 int forceMono = False;
994 extern Option chatOptions[]; // FIXME: adapt Chat window, removing ICS pane and Hide button
995 chatOptions[6].type = chatOptions[10].type = Skip;
997 srandom(time(0)); // [HGM] book: make random truly random
999 setbuf(stdout, NULL);
1000 setbuf(stderr, NULL);
1003 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1004 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
1008 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1013 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
1014 typedef struct {char *name, *value; } Config;
1015 static Config configList[] = {
1016 { "Datadir", DATADIR },
1017 { "Sysconfdir", SYSCONFDIR },
1022 for(i=0; configList[i].name; i++) {
1023 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
1024 if(argc > 2) printf("%s", configList[i].value);
1025 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
1030 programName = strrchr(argv[0], '/');
1031 if (programName == NULL)
1032 programName = argv[0];
1037 XtSetLanguageProc(NULL, NULL, NULL);
1038 if (appData.debugMode) {
1039 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1042 bindtextdomain(PACKAGE, LOCALEDIR);
1043 textdomain(PACKAGE);
1046 appData.boardSize = "";
1047 InitAppData(ConvertToLine(argc, argv));
1049 if (p == NULL) p = "/tmp";
1050 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1051 gameCopyFilename = (char*) malloc(i);
1052 gamePasteFilename = (char*) malloc(i);
1053 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1054 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1056 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1057 static char buf[MSG_SIZ];
1058 EscapeExpand(buf, appData.firstInitString);
1059 appData.firstInitString = strdup(buf);
1060 EscapeExpand(buf, appData.secondInitString);
1061 appData.secondInitString = strdup(buf);
1062 EscapeExpand(buf, appData.firstComputerString);
1063 appData.firstComputerString = strdup(buf);
1064 EscapeExpand(buf, appData.secondComputerString);
1065 appData.secondComputerString = strdup(buf);
1068 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1071 if (chdir(chessDir) != 0) {
1072 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1078 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1079 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1080 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1081 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1084 setbuf(debugFP, NULL);
1087 /* [HGM,HR] make sure board size is acceptable */
1088 if(appData.NrFiles > BOARD_FILES ||
1089 appData.NrRanks > BOARD_RANKS )
1090 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1093 /* This feature does not work; animation needs a rewrite */
1094 appData.highlightDragging = FALSE;
1098 gameInfo.variant = StringToVariant(appData.variant);
1099 InitPosition(FALSE);
1102 XtAppInitialize(&appContext, "XBoard", shellOptions,
1103 XtNumber(shellOptions),
1104 &argc, argv, xboardResources, NULL, 0);
1106 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1107 clientResources, XtNumber(clientResources),
1110 xDisplay = XtDisplay(shellWidget);
1111 xScreen = DefaultScreen(xDisplay);
1112 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1115 * determine size, based on supplied or remembered -size, or screen size
1117 if (isdigit(appData.boardSize[0])) {
1118 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1119 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1120 &fontPxlSize, &smallLayout, &tinyLayout);
1122 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1123 programName, appData.boardSize);
1127 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // scale height
1129 /* Find some defaults; use the nearest known size */
1130 SizeDefaults *szd, *nearest;
1131 int distance = 99999;
1132 nearest = szd = sizeDefaults;
1133 while (szd->name != NULL) {
1134 if (abs(szd->squareSize - squareSize) < distance) {
1136 distance = abs(szd->squareSize - squareSize);
1137 if (distance == 0) break;
1141 if (i < 2) lineGap = nearest->lineGap;
1142 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1143 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1144 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1145 if (i < 6) smallLayout = nearest->smallLayout;
1146 if (i < 7) tinyLayout = nearest->tinyLayout;
1149 SizeDefaults *szd = sizeDefaults;
1150 if (*appData.boardSize == NULLCHAR) {
1151 while (DisplayWidth(xDisplay, xScreen) < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1152 DisplayHeight(xDisplay, xScreen) < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1155 if (szd->name == NULL) szd--;
1156 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1158 while (szd->name != NULL &&
1159 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1160 if (szd->name == NULL) {
1161 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1162 programName, appData.boardSize);
1166 squareSize = szd->squareSize;
1167 lineGap = szd->lineGap;
1168 clockFontPxlSize = szd->clockFontPxlSize;
1169 coordFontPxlSize = szd->coordFontPxlSize;
1170 fontPxlSize = szd->fontPxlSize;
1171 smallLayout = szd->smallLayout;
1172 tinyLayout = szd->tinyLayout;
1173 // [HGM] font: use defaults from settings file if available and not overruled
1176 defaultLineGap = lineGap;
1177 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1179 /* [HR] height treated separately (hacked) */
1180 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1181 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1184 * Determine what fonts to use.
1186 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1189 * Detect if there are not enough colors available and adapt.
1191 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1192 appData.monoMode = True;
1195 forceMono = MakeColors();
1198 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1200 appData.monoMode = True;
1203 if (appData.monoMode && appData.debugMode) {
1204 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1205 (unsigned long) XWhitePixel(xDisplay, xScreen),
1206 (unsigned long) XBlackPixel(xDisplay, xScreen));
1209 ParseIcsTextColors();
1211 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1217 layoutName = "tinyLayout";
1218 } else if (smallLayout) {
1219 layoutName = "smallLayout";
1221 layoutName = "normalLayout";
1224 optList = BoardPopUp(squareSize, lineGap, (void*)
1230 InitDrawingHandle(optList + W_BOARD);
1231 currBoard = &optList[W_BOARD];
1232 boardWidget = optList[W_BOARD].handle;
1233 menuBarWidget = optList[W_MENU].handle;
1234 dropMenu = optList[W_DROP].handle;
1235 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1236 formWidget = XtParent(boardWidget);
1237 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1238 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1239 XtGetValues(optList[W_WHITE].handle, args, 2);
1240 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1241 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1242 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1243 XtGetValues(optList[W_PAUSE].handle, args, 2);
1246 xBoardWindow = XtWindow(boardWidget);
1248 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1249 // not need to go into InitDrawingSizes().
1252 * Create X checkmark bitmap and initialize option menu checks.
1254 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1255 checkmark_bits, checkmark_width, checkmark_height);
1261 ReadBitmap(&wIconPixmap, "icon_white.bm",
1262 icon_white_bits, icon_white_width, icon_white_height);
1263 ReadBitmap(&bIconPixmap, "icon_black.bm",
1264 icon_black_bits, icon_black_width, icon_black_height);
1265 iconPixmap = wIconPixmap;
1267 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1268 XtSetValues(shellWidget, args, i);
1271 * Create a cursor for the board widget.
1273 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1274 XChangeWindowAttributes(xDisplay, xBoardWindow,
1275 CWCursor, &window_attributes);
1278 * Inhibit shell resizing.
1280 shellArgs[0].value = (XtArgVal) &w;
1281 shellArgs[1].value = (XtArgVal) &h;
1282 XtGetValues(shellWidget, shellArgs, 2);
1283 shellArgs[4].value = shellArgs[2].value = w;
1284 shellArgs[5].value = shellArgs[3].value = h;
1285 // XtSetValues(shellWidget, &shellArgs[2], 4);
1286 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1287 marginH = h - boardHeight;
1289 CatchDeleteWindow(shellWidget, "QuitProc");
1294 if(appData.logoSize)
1295 { // locate and read user logo
1296 char buf[MSG_SIZ], name[MSG_SIZ];
1297 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1298 if(!FindLogo(name, ".logo", buf))
1299 FindLogo(appData.logoDir, name + 6, buf);
1300 ASSIGN(userLogo, buf);
1303 if (appData.animate || appData.animateDragging)
1307 char *TranslationsTableMenus=GenerateGlobalTranslationTable ();
1309 XtAugmentTranslations(formWidget,
1310 XtParseTranslationTable(globalTranslations));
1311 XtAugmentTranslations(formWidget,
1312 XtParseTranslationTable(TranslationsTableMenus));
1314 XtAddEventHandler(formWidget, KeyPressMask, False,
1315 (XtEventHandler) MoveTypeInProc, NULL);
1316 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1317 (XtEventHandler) EventProc, NULL);
1319 /* [AS] Restore layout */
1320 if( wpMoveHistory.visible ) {
1324 if( wpEvalGraph.visible )
1329 if( wpEngineOutput.visible ) {
1330 EngineOutputPopUp();
1333 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1338 if (errorExitStatus == -1) {
1339 if (appData.icsActive) {
1340 /* We now wait until we see "login:" from the ICS before
1341 sending the logon script (problems with timestamp otherwise) */
1342 /*ICSInitScript();*/
1343 if (appData.icsInputBox) ICSInputBoxPopUp();
1347 signal(SIGWINCH, TermSizeSigHandler);
1349 signal(SIGINT, IntSigHandler);
1350 signal(SIGTERM, IntSigHandler);
1351 if (*appData.cmailGameName != NULLCHAR) {
1352 signal(SIGUSR1, CmailSigHandler);
1357 // XtSetKeyboardFocus(shellWidget, formWidget);
1358 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1360 XtAppMainLoop(appContext);
1361 if (appData.debugMode) fclose(debugFP); // [DM] debug
1369 while((m = XtAppPending(appContext))) XtAppProcessEvent(appContext, m);
1373 TermSizeSigHandler (int sig)
1379 IntSigHandler (int sig)
1385 CmailSigHandler (int sig)
1390 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1392 /* Activate call-back function CmailSigHandlerCallBack() */
1393 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1395 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1399 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1402 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1404 /**** end signal code ****/
1407 #define Abs(n) ((n)<0 ? -(n) : (n))
1411 InsertPxlSize (char *pattern, int targetPxlSize)
1413 char *base_fnt_lst, strInt[12], *p, *q;
1414 int alternatives, i, len, strIntLen;
1417 * Replace the "*" (if present) in the pixel-size slot of each
1418 * alternative with the targetPxlSize.
1422 while ((p = strchr(p, ',')) != NULL) {
1426 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1427 strIntLen = strlen(strInt);
1428 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1432 while (alternatives--) {
1433 char *comma = strchr(p, ',');
1434 for (i=0; i<14; i++) {
1435 char *hyphen = strchr(p, '-');
1437 if (comma && hyphen > comma) break;
1438 len = hyphen + 1 - p;
1439 if (i == 7 && *p == '*' && len == 2) {
1441 memcpy(q, strInt, strIntLen);
1451 len = comma + 1 - p;
1458 return base_fnt_lst;
1462 CreateFontSet (char *base_fnt_lst)
1465 char **missing_list;
1469 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1470 &missing_list, &missing_count, &def_string);
1471 if (appData.debugMode) {
1473 XFontStruct **font_struct_list;
1474 char **font_name_list;
1475 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1477 fprintf(debugFP, " got list %s, locale %s\n",
1478 XBaseFontNameListOfFontSet(fntSet),
1479 XLocaleOfFontSet(fntSet));
1480 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1481 for (i = 0; i < count; i++) {
1482 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1485 for (i = 0; i < missing_count; i++) {
1486 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1489 if (fntSet == NULL) {
1490 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1495 #else // not ENABLE_NLS
1497 * Find a font that matches "pattern" that is as close as
1498 * possible to the targetPxlSize. Prefer fonts that are k
1499 * pixels smaller to fonts that are k pixels larger. The
1500 * pattern must be in the X Consortium standard format,
1501 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1502 * The return value should be freed with XtFree when no
1506 FindFont (char *pattern, int targetPxlSize)
1508 char **fonts, *p, *best, *scalable, *scalableTail;
1509 int i, j, nfonts, minerr, err, pxlSize;
1511 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1513 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1514 programName, pattern);
1521 for (i=0; i<nfonts; i++) {
1524 if (*p != '-') continue;
1526 if (*p == NULLCHAR) break;
1527 if (*p++ == '-') j++;
1529 if (j < 7) continue;
1532 scalable = fonts[i];
1535 err = pxlSize - targetPxlSize;
1536 if (Abs(err) < Abs(minerr) ||
1537 (minerr > 0 && err < 0 && -err == minerr)) {
1543 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1544 /* If the error is too big and there is a scalable font,
1545 use the scalable font. */
1546 int headlen = scalableTail - scalable;
1547 p = (char *) XtMalloc(strlen(scalable) + 10);
1548 while (isdigit(*scalableTail)) scalableTail++;
1549 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1551 p = (char *) XtMalloc(strlen(best) + 2);
1552 safeStrCpy(p, best, strlen(best)+1 );
1554 if (appData.debugMode) {
1555 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1556 pattern, targetPxlSize, p);
1558 XFreeFontNames(fonts);
1564 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1567 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1573 MarkMenuItem (char *menuRef, int state)
1575 MenuItem *item = MenuNameToItem(menuRef);
1579 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1580 XtSetValues(item->handle, args, 1);
1585 EnableNamedMenuItem (char *menuRef, int state)
1587 MenuItem *item = MenuNameToItem(menuRef);
1589 if(item) XtSetSensitive(item->handle, state);
1593 EnableButtonBar (int state)
1595 XtSetSensitive(optList[W_BUTTON].handle, state);
1600 SetMenuEnables (Enables *enab)
1602 while (enab->name != NULL) {
1603 EnableNamedMenuItem(enab->name, enab->value);
1609 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1610 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1612 if(*nprms == 0) return;
1613 item = MenuNameToItem(prms[0]);
1614 if(item) ((MenuProc *) item->proc) ();
1626 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1627 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1628 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1629 dmEnables[i].piece);
1630 XtSetSensitive(entry, p != NULL || !appData.testLegality
1631 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1632 && !appData.icsActive));
1634 while (p && *p++ == dmEnables[i].piece) count++;
1635 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1637 XtSetArg(args[j], XtNlabel, label); j++;
1638 XtSetValues(entry, args, j);
1643 do_flash_delay (unsigned long msec)
1649 FlashDelay (int flash_delay)
1651 XSync(xDisplay, False);
1652 if(flash_delay) do_flash_delay(flash_delay);
1656 Fraction (int x, int start, int stop)
1658 double f = ((double) x - start)/(stop - start);
1659 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1663 static WindowPlacement wpNew;
1666 CoDrag (Widget sh, WindowPlacement *wp)
1669 int j=0, touch=0, fudge = 2;
1670 GetActualPlacement(sh, wp);
1671 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1672 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1673 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1674 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1675 if(!touch ) return; // only windows that touch co-move
1676 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1677 int heightInc = wpNew.height - wpMain.height;
1678 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1679 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1680 wp->y += fracTop * heightInc;
1681 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1682 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1683 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1684 int widthInc = wpNew.width - wpMain.width;
1685 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1686 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1687 wp->y += fracLeft * widthInc;
1688 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1689 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1691 wp->x += wpNew.x - wpMain.x;
1692 wp->y += wpNew.y - wpMain.y;
1693 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1694 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1695 XtSetArg(args[j], XtNx, wp->x); j++;
1696 XtSetArg(args[j], XtNy, wp->y); j++;
1697 XtSetValues(sh, args, j);
1701 ReSize (WindowPlacement *wp)
1704 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1705 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1706 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1707 if(sqy < sqx) sqx = sqy;
1708 if(sqx != squareSize) {
1709 squareSize = sqx; // adopt new square size
1710 CreatePNGPieces(); // make newly scaled pieces
1711 InitDrawingSizes(0, 0); // creates grid etc.
1712 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1713 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1714 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1715 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1716 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1719 static XtIntervalId delayedDragID = 0;
1728 GetActualPlacement(shellWidget, &wpNew);
1729 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1730 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1731 busy = 0; return; // false alarm
1734 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1735 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1736 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1737 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1739 DrawPosition(True, NULL);
1740 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1748 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1750 XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1754 EventProc (Widget widget, caddr_t unused, XEvent *event)
1756 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1757 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1761 * event handler for redrawing the board
1764 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1766 DrawPosition(True, NULL);
1771 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1772 { // [HGM] pv: walk PV
1773 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1776 extern int savedIndex; /* gross that this is global */
1779 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1782 XawTextPosition index, dummy;
1785 XawTextGetSelectionPos(w, &index, &dummy);
1786 XtSetArg(arg, XtNstring, &val);
1787 XtGetValues(w, &arg, 1);
1788 ReplaceComment(savedIndex, val);
1789 if(savedIndex != currentMove) ToNrEvent(savedIndex);
1790 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1794 /* Disable all user input other than deleting the window */
1795 static int frozen = 0;
1801 /* Grab by a widget that doesn't accept input */
1802 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1806 /* Undo a FreezeUI */
1810 if (!frozen) return;
1811 XtRemoveGrab(optList[W_MESSG].handle);
1819 static int oldPausing = FALSE;
1820 static GameMode oldMode = (GameMode) -1;
1823 if (!boardWidget || !XtIsRealized(boardWidget)) return;
1825 if (pausing != oldPausing) {
1826 oldPausing = pausing;
1827 MarkMenuItem("Mode.Pause", pausing);
1829 if (appData.showButtonBar) {
1830 /* Always toggle, don't set. Previous code messes up when
1831 invoked while the button is pressed, as releasing it
1832 toggles the state again. */
1835 XtSetArg(args[0], XtNbackground, &oldbg);
1836 XtSetArg(args[1], XtNforeground, &oldfg);
1837 XtGetValues(optList[W_PAUSE].handle,
1839 XtSetArg(args[0], XtNbackground, oldfg);
1840 XtSetArg(args[1], XtNforeground, oldbg);
1842 XtSetValues(optList[W_PAUSE].handle, args, 2);
1846 wname = ModeToWidgetName(oldMode);
1847 if (wname != NULL) {
1848 MarkMenuItem(wname, False);
1850 wname = ModeToWidgetName(gameMode);
1851 if (wname != NULL) {
1852 MarkMenuItem(wname, True);
1854 if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
1855 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]);