2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
9 * Software Foundation, Inc.
11 * The following terms apply to Digital Equipment Corporation's copyright
13 * ------------------------------------------------------------------------
16 * Permission to use, copy, modify, and distribute this software and its
17 * documentation for any purpose and without fee is hereby granted,
18 * provided that the above copyright notice appear in all copies and that
19 * both that copyright notice and this permission notice appear in
20 * supporting documentation, and that the name of Digital not be
21 * used in advertising or publicity pertaining to distribution of the
22 * software without specific, written prior permission.
24 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
26 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
27 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
28 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
29 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
31 * ------------------------------------------------------------------------
33 * The following terms apply to the enhanced version of XBoard
34 * distributed by the Free Software Foundation:
35 * ------------------------------------------------------------------------
37 * GNU XBoard is free software: you can redistribute it and/or modify
38 * it under the terms of the GNU General Public License as published by
39 * the Free Software Foundation, either version 3 of the License, or (at
40 * your option) any later version.
42 * GNU XBoard is distributed in the hope that it will be useful, but
43 * WITHOUT ANY WARRANTY; without even the implied warranty of
44 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
45 * General Public License for more details.
47 * You should have received a copy of the GNU General Public License
48 * along with this program. If not, see http://www.gnu.org/licenses/. *
50 *------------------------------------------------------------------------
51 ** See the file ChangeLog for a revision history. */
61 #include <sys/types.h>
65 #include <cairo/cairo.h>
66 #include <cairo/cairo-xlib.h>
69 # if HAVE_SYS_SOCKET_H
70 # include <sys/socket.h>
71 # include <netinet/in.h>
73 # else /* not HAVE_SYS_SOCKET_H */
74 # if HAVE_LAN_SOCKET_H
75 # include <lan/socket.h>
77 # include <lan/netdb.h>
78 # else /* not HAVE_LAN_SOCKET_H */
79 # define OMIT_SOCKETS 1
80 # endif /* not HAVE_LAN_SOCKET_H */
81 # endif /* not HAVE_SYS_SOCKET_H */
82 #endif /* !OMIT_SOCKETS */
87 #else /* not STDC_HEADERS */
88 extern char *getenv();
91 # else /* not HAVE_STRING_H */
93 # endif /* not HAVE_STRING_H */
94 #endif /* not STDC_HEADERS */
97 # include <sys/fcntl.h>
98 #else /* not HAVE_SYS_FCNTL_H */
101 # endif /* HAVE_FCNTL_H */
102 #endif /* not HAVE_SYS_FCNTL_H */
104 #if HAVE_SYS_SYSTEMINFO_H
105 # include <sys/systeminfo.h>
106 #endif /* HAVE_SYS_SYSTEMINFO_H */
108 #if TIME_WITH_SYS_TIME
109 # include <sys/time.h>
113 # include <sys/time.h>
124 # include <sys/wait.h>
129 # define NAMLEN(dirent) strlen((dirent)->d_name)
130 # define HAVE_DIR_STRUCT
132 # define dirent direct
133 # define NAMLEN(dirent) (dirent)->d_namlen
135 # include <sys/ndir.h>
136 # define HAVE_DIR_STRUCT
139 # include <sys/dir.h>
140 # define HAVE_DIR_STRUCT
144 # define HAVE_DIR_STRUCT
152 #include <X11/keysym.h>
153 #include <X11/Intrinsic.h>
154 #include <X11/StringDefs.h>
155 #include <X11/Shell.h>
156 #include <X11/cursorfont.h>
157 #include <X11/Xatom.h>
158 #include <X11/Xmu/Atoms.h>
160 #include <X11/Xaw3d/Dialog.h>
161 #include <X11/Xaw3d/Form.h>
162 #include <X11/Xaw3d/List.h>
163 #include <X11/Xaw3d/Label.h>
164 #include <X11/Xaw3d/SimpleMenu.h>
165 #include <X11/Xaw3d/SmeBSB.h>
166 #include <X11/Xaw3d/SmeLine.h>
167 #include <X11/Xaw3d/Box.h>
168 #include <X11/Xaw3d/MenuButton.h>
169 #include <X11/Xaw3d/Text.h>
170 #include <X11/Xaw3d/AsciiText.h>
172 #include <X11/Xaw/Dialog.h>
173 #include <X11/Xaw/Form.h>
174 #include <X11/Xaw/List.h>
175 #include <X11/Xaw/Label.h>
176 #include <X11/Xaw/SimpleMenu.h>
177 #include <X11/Xaw/SmeBSB.h>
178 #include <X11/Xaw/SmeLine.h>
179 #include <X11/Xaw/Box.h>
180 #include <X11/Xaw/MenuButton.h>
181 #include <X11/Xaw/Text.h>
182 #include <X11/Xaw/AsciiText.h>
185 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
188 #include "bitmaps/icon_white.bm"
189 #include "bitmaps/icon_black.bm"
190 #include "bitmaps/checkmark.bm"
192 #include "frontend.h"
194 #include "backendz.h"
198 #include "xgamelist.h"
199 #include "xhistory.h"
203 #include "engineoutput.h"
214 #define usleep(t) _sleep2(((t)+500)/1000)
218 # define _(s) gettext (s)
219 # define N_(s) gettext_noop (s)
225 int main P((int argc, char **argv));
226 RETSIGTYPE CmailSigHandler P((int sig));
227 RETSIGTYPE IntSigHandler P((int sig));
228 RETSIGTYPE TermSizeSigHandler P((int sig));
229 Widget CreateMenuBar P((Menu *mb, int boardWidth));
231 char *InsertPxlSize P((char *pattern, int targetPxlSize));
232 XFontSet CreateFontSet P((char *base_fnt_lst));
234 char *FindFont P((char *pattern, int targetPxlSize));
236 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
237 u_int wreq, u_int hreq));
238 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
239 void DelayedDrag P((void));
240 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
241 void HandlePV P((Widget w, XEvent * event,
242 String * params, Cardinal * nParams));
243 void DrawPositionProc P((Widget w, XEvent *event,
244 String *prms, Cardinal *nprms));
245 void CommentClick P((Widget w, XEvent * event,
246 String * params, Cardinal * nParams));
247 void ICSInputBoxPopUp P((void));
248 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
249 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
250 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
251 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
252 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
253 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
254 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
255 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
256 Boolean TempBackwardActive = False;
257 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
258 void DisplayMove P((int moveNumber));
259 void update_ics_width P(());
260 int CopyMemoProc P(());
261 static int FindLogo P((char *place, char *name, char *buf));
264 * XBoard depends on Xt R4 or higher
266 int xtVersion = XtSpecificationRelease;
271 Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
272 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
273 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
274 Option *optList; // contains all widgets of main window
276 XFontSet fontSet, clockFontSet;
279 XFontStruct *clockFontStruct;
281 Font coordFontID, countFontID;
282 XFontStruct *coordFontStruct, *countFontStruct;
283 XtAppContext appContext;
286 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
288 Position commentX = -1, commentY = -1;
289 Dimension commentW, commentH;
290 typedef unsigned int BoardSize;
292 Boolean chessProgram;
294 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
295 int smallLayout = 0, tinyLayout = 0,
296 marginW, marginH, // [HGM] for run-time resizing
297 fromX = -1, fromY = -1, toX, toY, commentUp = False,
298 errorExitStatus = -1, defaultLineGap;
299 Dimension textHeight;
300 Pixel timerForegroundPixel, timerBackgroundPixel;
301 Pixel buttonForegroundPixel, buttonBackgroundPixel;
302 char *chessDir, *programName, *programVersion;
303 Boolean alwaysOnTop = False;
304 char *icsTextMenuString;
306 char *firstChessProgramNames;
307 char *secondChessProgramNames;
309 WindowPlacement wpMain;
310 WindowPlacement wpConsole;
311 WindowPlacement wpComment;
312 WindowPlacement wpMoveHistory;
313 WindowPlacement wpEvalGraph;
314 WindowPlacement wpEngineOutput;
315 WindowPlacement wpGameList;
316 WindowPlacement wpTags;
317 WindowPlacement wpDualBoard;
320 /* This magic number is the number of intermediate frames used
321 in each half of the animation. For short moves it's reduced
322 by 1. The total number of frames will be factor * 2 + 1. */
325 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
332 DropMenuEnables dmEnables[] = {
349 XtResource clientResources[] = {
350 { "flashCount", "flashCount", XtRInt, sizeof(int),
351 XtOffset(AppDataPtr, flashCount), XtRImmediate,
352 (XtPointer) FLASH_COUNT },
355 XrmOptionDescRec shellOptions[] = {
356 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
357 { "-flash", "flashCount", XrmoptionNoArg, "3" },
358 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
361 XtActionsRec boardActions[] = {
362 { "DrawPosition", DrawPositionProc },
363 { "HandlePV", HandlePV },
364 { "SelectPV", SelectPV },
365 { "StopPV", StopPV },
366 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
367 { "QuitProc", QuitWrapper },
368 { "ManProc", ManInner },
369 { "TempBackwardProc", TempBackwardProc },
370 { "TempForwardProc", TempForwardProc },
371 { "CommentClick", (XtActionProc) CommentClick },
372 { "GenericPopDown", (XtActionProc) GenericPopDown },
373 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
374 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
375 { "SelectMove", (XtActionProc) SelectMoveX },
376 { "LoadSelectedProc", LoadSelectedProc },
377 { "SetFilterProc", SetFilterProc },
378 { "TypeInProc", TypeInProc },
379 { "EnterKeyProc", EnterKeyProc },
380 { "UpKeyProc", UpKeyProc },
381 { "DownKeyProc", DownKeyProc },
382 { "WheelProc", WheelProc },
383 { "TabProc", TabProc },
386 char globalTranslations[] =
387 ":Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
388 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
389 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
390 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
391 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
392 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
393 :<Key>Pause: MenuItem(Mode.Pause) \n \
394 :Ctrl<Key>d: MenuItem(DebugProc) \n \
395 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
396 :<Key>Left: MenuItem(Edit.Backward) \n \
397 :<Key>Right: MenuItem(Edit.Forward) \n \
398 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
399 #ifndef OPTIONSDIALOG
401 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
402 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
403 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
404 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
405 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
408 :<KeyDown>Return: TempBackwardProc() \n \
409 :<KeyUp>Return: TempForwardProc() \n";
411 char ICSInputTranslations[] =
412 "<Key>Up: UpKeyProc() \n "
413 "<Key>Down: DownKeyProc() \n "
414 "<Key>Return: EnterKeyProc() \n";
416 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
417 // as the widget is destroyed before the up-click can call extend-end
418 char commentTranslations[] = "<Btn3Down>: extend-end(PRIMARY) select-start() CommentClick() \n";
420 String xboardResources[] = {
421 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
426 /* Max possible square size */
427 #define MAXSQSIZE 256
429 /* Arrange to catch delete-window events */
430 Atom wm_delete_window;
432 CatchDeleteWindow (Widget w, String procname)
435 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
436 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
437 XtAugmentTranslations(w, XtParseTranslationTable(buf));
444 XtSetArg(args[0], XtNiconic, False);
445 XtSetValues(shellWidget, args, 1);
447 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
450 //---------------------------------------------------------------------------------------------------------
451 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
454 #define CW_USEDEFAULT (1<<31)
455 #define ICS_TEXT_MENU_SIZE 90
456 #define DEBUG_FILE "xboard.debug"
457 #define SetCurrentDirectory chdir
458 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
462 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
465 // front-end part of option handling
467 // [HGM] This platform-dependent table provides the location for storing the color info
468 extern char *crWhite, * crBlack;
472 &appData.whitePieceColor,
473 &appData.blackPieceColor,
474 &appData.lightSquareColor,
475 &appData.darkSquareColor,
476 &appData.highlightSquareColor,
477 &appData.premoveHighlightColor,
478 &appData.lowTimeWarningColor,
489 // [HGM] font: keep a font for each square size, even non-stndard ones
492 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
493 char *fontTable[NUM_FONTS][MAX_SIZE];
496 ParseFont (char *name, int number)
497 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
499 if(sscanf(name, "size%d:", &size)) {
500 // [HGM] font: font is meant for specific boardSize (likely from settings file);
501 // defer processing it until we know if it matches our board size
502 if(strstr(name, "-*-") && // only pay attention to things that look like X-fonts
503 size >= 0 && size<MAX_SIZE) { // for now, fixed limit
504 fontTable[number][size] = strdup(strchr(name, ':')+1);
505 fontValid[number][size] = True;
510 case 0: // CLOCK_FONT
511 appData.clockFont = strdup(name);
513 case 1: // MESSAGE_FONT
514 appData.font = strdup(name);
516 case 2: // COORD_FONT
517 appData.coordFont = strdup(name);
522 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
527 { // only 2 fonts currently
528 appData.clockFont = CLOCK_FONT_NAME;
529 appData.coordFont = COORD_FONT_NAME;
530 appData.font = DEFAULT_FONT_NAME;
535 { // no-op, until we identify the code for this already in XBoard and move it here
539 ParseColor (int n, char *name)
540 { // in XBoard, just copy the color-name string
541 if(colorVariable[n] && *name == '#') *(char**)colorVariable[n] = strdup(name);
547 return *(char**)colorVariable[n];
551 ParseTextAttribs (ColorClass cc, char *s)
553 (&appData.colorShout)[cc] = strdup(s);
557 ParseBoardSize (void *addr, char *name)
559 appData.boardSize = strdup(name);
564 { // In XBoard the sound-playing program takes care of obtaining the actual sound
568 SetCommPortDefaults ()
569 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
572 // [HGM] args: these three cases taken out to stay in front-end
574 SaveFontArg (FILE *f, ArgDescriptor *ad)
577 int i, n = (int)(intptr_t)ad->argLoc;
579 case 0: // CLOCK_FONT
580 name = appData.clockFont;
582 case 1: // MESSAGE_FONT
585 case 2: // COORD_FONT
586 name = appData.coordFont;
591 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
592 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
593 fontTable[n][squareSize] = strdup(name);
594 fontValid[n][squareSize] = True;
597 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
598 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
603 { // nothing to do, as the sounds are at all times represented by their text-string names already
607 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
608 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
609 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
613 SaveColor (FILE *f, ArgDescriptor *ad)
614 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
615 if(colorVariable[(int)(intptr_t)ad->argLoc])
616 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
620 SaveBoardSize (FILE *f, char *name, void *addr)
621 { // wrapper to shield back-end from BoardSize & sizeInfo
622 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
626 ParseCommPortSettings (char *s)
627 { // no such option in XBoard (yet)
633 GetActualPlacement (Widget wg, WindowPlacement *wp)
635 XWindowAttributes winAt;
642 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
643 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
644 wp->x = rx - winAt.x;
645 wp->y = ry - winAt.y;
646 wp->height = winAt.height;
647 wp->width = winAt.width;
648 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
652 GetPlacement (DialogClass dlg, WindowPlacement *wp)
653 { // wrapper to shield back-end from widget type
654 if(shellUp[dlg]) GetActualPlacement(shells[dlg], wp);
659 { // wrapper to shield use of window handles from back-end (make addressible by number?)
660 // In XBoard this will have to wait until awareness of window parameters is implemented
661 GetActualPlacement(shellWidget, &wpMain);
662 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
663 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
664 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
665 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
666 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
667 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
671 PrintCommPortSettings (FILE *f, char *name)
672 { // This option does not exist in XBoard
676 EnsureOnScreen (int *x, int *y, int minX, int minY)
683 { // [HGM] args: allows testing if main window is realized from back-end
684 return xBoardWindow != 0;
688 PopUpStartupDialog ()
689 { // start menu not implemented in XBoard
693 ConvertToLine (int argc, char **argv)
695 static char line[128*1024], buf[1024];
699 for(i=1; i<argc; i++)
701 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
702 && argv[i][0] != '{' )
703 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
705 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
706 strncat(line, buf, 128*1024 - strlen(line) - 1 );
709 line[strlen(line)-1] = NULLCHAR;
713 //--------------------------------------------------------------------------------------------
716 ResizeBoardWindow (int w, int h, int inhibit)
718 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
720 shellArgs[0].value = w;
721 shellArgs[1].value = h;
722 shellArgs[4].value = shellArgs[2].value = w;
723 shellArgs[5].value = shellArgs[3].value = h;
724 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
726 XSync(xDisplay, False);
730 MakeOneColor (char *name, Pixel *color)
733 if (!appData.monoMode) {
734 vFrom.addr = (caddr_t) name;
735 vFrom.size = strlen(name);
736 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
737 if (vTo.addr == NULL) {
738 appData.monoMode = True;
741 *color = *(Pixel *) vTo.addr;
749 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
750 int forceMono = False;
752 if (appData.lowTimeWarning)
753 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
754 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
755 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
761 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
762 { // detervtomine what fonts to use, and create them
766 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
767 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
768 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
769 appData.font = fontTable[MESSAGE_FONT][squareSize];
770 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
771 appData.coordFont = fontTable[COORD_FONT][squareSize];
774 appData.font = InsertPxlSize(appData.font, fontPxlSize);
775 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
776 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
777 fontSet = CreateFontSet(appData.font);
778 clockFontSet = CreateFontSet(appData.clockFont);
780 /* For the coordFont, use the 0th font of the fontset. */
781 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
782 XFontStruct **font_struct_list;
783 XFontSetExtents *fontSize;
784 char **font_name_list;
785 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
786 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
787 coordFontStruct = XQueryFont(xDisplay, coordFontID);
788 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
789 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
792 appData.font = FindFont(appData.font, fontPxlSize);
793 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
794 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
795 clockFontID = XLoadFont(xDisplay, appData.clockFont);
796 clockFontStruct = XQueryFont(xDisplay, clockFontID);
797 coordFontID = XLoadFont(xDisplay, appData.coordFont);
798 coordFontStruct = XQueryFont(xDisplay, coordFontID);
799 // textHeight in !NLS mode!
801 countFontID = coordFontID; // [HGM] holdings
802 countFontStruct = coordFontStruct;
804 xdb = XtDatabase(xDisplay);
806 XrmPutLineResource(&xdb, "*international: True");
807 vTo.size = sizeof(XFontSet);
808 vTo.addr = (XtPointer) &fontSet;
809 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
811 XrmPutStringResource(&xdb, "*font", appData.font);
821 case ArgInt: p = " N"; break;
822 case ArgString: p = " STR"; break;
823 case ArgBoolean: p = " TF"; break;
824 case ArgSettingsFilename:
825 case ArgBackupSettingsFile:
826 case ArgFilename: p = " FILE"; break;
827 case ArgX: p = " Nx"; break;
828 case ArgY: p = " Ny"; break;
829 case ArgAttribs: p = " TEXTCOL"; break;
830 case ArgColor: p = " COL"; break;
831 case ArgFont: p = " FONT"; break;
832 case ArgBoardSize: p = " SIZE"; break;
833 case ArgFloat: p = " FLOAT"; break;
838 case ArgCommSettings:
847 GenerateGlobalTranslationTable (void)
849 /* go through all menu items and extract the keyboard shortcuts, so that X11 can load them */
855 output[0] = strdup(""); // build keystrokes with and wo mod keys separately
856 output[1] = strdup(""); // so the more specific can preceed the other
858 /* loop over all menu entries */
859 for( i=0; menuBar[i-n].mi || !n++; i++)
861 mi = menuBar[i+n].mi; // kludge to access 'noMenu' behind sentinel
862 for(j=0; mi[j].proc; j++)
870 char *key,*test, *mods;
872 /* check for Ctrl/Alt */
873 if( strstr(mi[j].accel, "<Ctrl>") ) ctrl = 1;
874 if( strstr(mi[j].accel, "<Shift>") ) shift = 1;
875 if( strstr(mi[j].accel, "<Alt>") ) alt = 1;
877 /* remove all <...> */
878 test = strrchr(mi[j].accel, '>');
880 key = strdup(mi[j].accel);
882 key = strdup(++test); // remove ">"
884 /* instead of shift X11 uses the uppercase letter directly*/
885 if (shift && strlen(key)==1 )
887 *key = toupper(*key);
891 /* handle some special cases which have different names in X11 */
892 if ( strncmp(key, "Page_Down", 9) == 0 )
897 else if ( strncmp(key, "Page_Up", 7) == 0 )
903 /* create string of mods */
905 mods = strdup("Ctrl ");
911 mods = realloc(mods, strlen(mods) + strlen("Meta ")+1);
912 strncat(mods, "Meta ", 5);
917 mods = realloc(mods, strlen(mods) + strlen("Shift ")+1);
918 strncat(mods, "Shift ", 6);
921 // remove trailing space
922 if( isspace(mods[strlen(mods)-1]) )
923 mods[strlen(mods)-1]='\0';
925 /* get the name for the callback, we can use MenuItem() here that will call KeyBindingProc */
926 char *name = malloc(MSG_SIZ);
927 if(n) snprintf(name, MSG_SIZ, "%s", mi[j].ref);
928 else snprintf(name, MSG_SIZ, "%s.%s", menuBar[i].ref, mi[j].ref);
930 char *buffer = malloc(MSG_SIZ);
931 snprintf(buffer, MSG_SIZ, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
933 /* add string to the output */
934 output[shift|alt|ctrl] = realloc(output[shift|alt|ctrl], strlen(output[shift|alt|ctrl]) + strlen(buffer)+1);
935 strncat(output[shift|alt|ctrl], buffer, strlen(buffer));
945 output[1] = realloc(output[1], strlen(output[1]) + strlen(output[0])+1);
946 strncat(output[1], output[0], strlen(output[0]));
957 ArgDescriptor *q, *p = argDescriptors+5;
958 printf("\nXBoard accepts the following options:\n"
959 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
960 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
961 " SIZE = board-size spec(s)\n"
962 " Within parentheses are short forms, or options to set to true or false.\n"
963 " Persistent options (saved in the settings file) are marked with *)\n\n");
965 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
966 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
967 if(p->save) strcat(buf+len, "*");
968 for(q=p+1; q->argLoc == p->argLoc; q++) {
969 if(q->argName[0] == '-') continue;
970 strcat(buf+len, q == p+1 ? " (" : " ");
971 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
973 if(q != p+1) strcat(buf+len, ")");
975 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
978 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
982 SlaveResize (Option *opt)
987 main (int argc, char **argv)
989 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
990 XSetWindowAttributes window_attributes;
992 Dimension boardWidth, boardHeight, w, h;
994 int forceMono = False;
996 extern Option chatOptions[]; // FIXME: adapt Chat window, removing ICS pane and Hide button
997 chatOptions[6].type = chatOptions[10].type = Skip;
999 srandom(time(0)); // [HGM] book: make random truly random
1001 setbuf(stdout, NULL);
1002 setbuf(stderr, NULL);
1005 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1006 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
1010 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1015 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
1016 typedef struct {char *name, *value; } Config;
1017 static Config configList[] = {
1018 { "Datadir", DATADIR },
1019 { "Sysconfdir", SYSCONFDIR },
1024 for(i=0; configList[i].name; i++) {
1025 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
1026 if(argc > 2) printf("%s", configList[i].value);
1027 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
1032 programName = strrchr(argv[0], '/');
1033 if (programName == NULL)
1034 programName = argv[0];
1039 XtSetLanguageProc(NULL, NULL, NULL);
1040 if (appData.debugMode) {
1041 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1044 bindtextdomain(PACKAGE, LOCALEDIR);
1045 textdomain(PACKAGE);
1048 appData.boardSize = "";
1049 InitAppData(ConvertToLine(argc, argv));
1051 if (p == NULL) p = "/tmp";
1052 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1053 gameCopyFilename = (char*) malloc(i);
1054 gamePasteFilename = (char*) malloc(i);
1055 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1056 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1058 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1059 static char buf[MSG_SIZ];
1060 EscapeExpand(buf, appData.firstInitString);
1061 appData.firstInitString = strdup(buf);
1062 EscapeExpand(buf, appData.secondInitString);
1063 appData.secondInitString = strdup(buf);
1064 EscapeExpand(buf, appData.firstComputerString);
1065 appData.firstComputerString = strdup(buf);
1066 EscapeExpand(buf, appData.secondComputerString);
1067 appData.secondComputerString = strdup(buf);
1070 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1073 if (chdir(chessDir) != 0) {
1074 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1080 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1081 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1082 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1083 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1086 setbuf(debugFP, NULL);
1089 /* [HGM,HR] make sure board size is acceptable */
1090 if(appData.NrFiles > BOARD_FILES ||
1091 appData.NrRanks > BOARD_RANKS )
1092 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1095 /* This feature does not work; animation needs a rewrite */
1096 appData.highlightDragging = FALSE;
1100 gameInfo.variant = StringToVariant(appData.variant);
1101 InitPosition(FALSE);
1104 XtAppInitialize(&appContext, "XBoard", shellOptions,
1105 XtNumber(shellOptions),
1106 &argc, argv, xboardResources, NULL, 0);
1108 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1109 clientResources, XtNumber(clientResources),
1112 xDisplay = XtDisplay(shellWidget);
1113 xScreen = DefaultScreen(xDisplay);
1114 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1117 * determine size, based on supplied or remembered -size, or screen size
1119 if (isdigit(appData.boardSize[0])) {
1120 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1121 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1122 &fontPxlSize, &smallLayout, &tinyLayout);
1124 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1125 programName, appData.boardSize);
1129 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // scale height
1131 /* Find some defaults; use the nearest known size */
1132 SizeDefaults *szd, *nearest;
1133 int distance = 99999;
1134 nearest = szd = sizeDefaults;
1135 while (szd->name != NULL) {
1136 if (abs(szd->squareSize - squareSize) < distance) {
1138 distance = abs(szd->squareSize - squareSize);
1139 if (distance == 0) break;
1143 if (i < 2) lineGap = nearest->lineGap;
1144 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1145 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1146 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1147 if (i < 6) smallLayout = nearest->smallLayout;
1148 if (i < 7) tinyLayout = nearest->tinyLayout;
1151 SizeDefaults *szd = sizeDefaults;
1152 if (*appData.boardSize == NULLCHAR) {
1153 while (DisplayWidth(xDisplay, xScreen) < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1154 DisplayHeight(xDisplay, xScreen) < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1157 if (szd->name == NULL) szd--;
1158 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1160 while (szd->name != NULL &&
1161 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1162 if (szd->name == NULL) {
1163 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1164 programName, appData.boardSize);
1168 squareSize = szd->squareSize;
1169 lineGap = szd->lineGap;
1170 clockFontPxlSize = szd->clockFontPxlSize;
1171 coordFontPxlSize = szd->coordFontPxlSize;
1172 fontPxlSize = szd->fontPxlSize;
1173 smallLayout = szd->smallLayout;
1174 tinyLayout = szd->tinyLayout;
1175 // [HGM] font: use defaults from settings file if available and not overruled
1178 defaultLineGap = lineGap;
1179 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1181 /* [HR] height treated separately (hacked) */
1182 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1183 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1186 * Determine what fonts to use.
1188 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1191 * Detect if there are not enough colors available and adapt.
1193 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1194 appData.monoMode = True;
1197 forceMono = MakeColors();
1200 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1202 appData.monoMode = True;
1205 if (appData.monoMode && appData.debugMode) {
1206 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1207 (unsigned long) XWhitePixel(xDisplay, xScreen),
1208 (unsigned long) XBlackPixel(xDisplay, xScreen));
1211 ParseIcsTextColors();
1213 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1219 layoutName = "tinyLayout";
1220 } else if (smallLayout) {
1221 layoutName = "smallLayout";
1223 layoutName = "normalLayout";
1226 optList = BoardPopUp(squareSize, lineGap, (void*)
1232 InitDrawingHandle(optList + W_BOARD);
1233 currBoard = &optList[W_BOARD];
1234 boardWidget = optList[W_BOARD].handle;
1235 menuBarWidget = optList[W_MENU].handle;
1236 dropMenu = optList[W_DROP].handle;
1237 titleWidget = optList[optList[W_TITLE].type != Skip ? W_TITLE : W_SMALL].handle;
1238 formWidget = XtParent(boardWidget);
1239 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1240 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1241 XtGetValues(optList[W_WHITE].handle, args, 2);
1242 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1243 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1244 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1245 XtGetValues(optList[W_PAUSE].handle, args, 2);
1248 xBoardWindow = XtWindow(boardWidget);
1250 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1251 // not need to go into InitDrawingSizes().
1254 * Create X checkmark bitmap and initialize option menu checks.
1256 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1257 checkmark_bits, checkmark_width, checkmark_height);
1263 ReadBitmap(&wIconPixmap, "icon_white.bm",
1264 icon_white_bits, icon_white_width, icon_white_height);
1265 ReadBitmap(&bIconPixmap, "icon_black.bm",
1266 icon_black_bits, icon_black_width, icon_black_height);
1267 iconPixmap = wIconPixmap;
1269 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1270 XtSetValues(shellWidget, args, i);
1273 * Create a cursor for the board widget.
1275 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1276 XChangeWindowAttributes(xDisplay, xBoardWindow,
1277 CWCursor, &window_attributes);
1280 * Inhibit shell resizing.
1282 shellArgs[0].value = (XtArgVal) &w;
1283 shellArgs[1].value = (XtArgVal) &h;
1284 XtGetValues(shellWidget, shellArgs, 2);
1285 shellArgs[4].value = shellArgs[2].value = w;
1286 shellArgs[5].value = shellArgs[3].value = h;
1287 // XtSetValues(shellWidget, &shellArgs[2], 4);
1288 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1289 marginH = h - boardHeight;
1291 CatchDeleteWindow(shellWidget, "QuitProc");
1296 if(appData.logoSize)
1297 { // locate and read user logo
1298 char buf[MSG_SIZ], name[MSG_SIZ];
1299 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1300 if(!FindLogo(name, ".logo", buf))
1301 FindLogo(appData.logoDir, name + 6, buf);
1302 ASSIGN(userLogo, buf);
1305 if (appData.animate || appData.animateDragging)
1309 char *TranslationsTableMenus=GenerateGlobalTranslationTable ();
1311 XtAugmentTranslations(formWidget,
1312 XtParseTranslationTable(globalTranslations));
1313 XtAugmentTranslations(formWidget,
1314 XtParseTranslationTable(TranslationsTableMenus));
1316 XtAddEventHandler(formWidget, KeyPressMask, False,
1317 (XtEventHandler) MoveTypeInProc, NULL);
1318 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1319 (XtEventHandler) EventProc, NULL);
1321 /* [AS] Restore layout */
1322 if( wpMoveHistory.visible ) {
1326 if( wpEvalGraph.visible )
1331 if( wpEngineOutput.visible ) {
1332 EngineOutputPopUp();
1335 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1340 if (errorExitStatus == -1) {
1341 if (appData.icsActive) {
1342 /* We now wait until we see "login:" from the ICS before
1343 sending the logon script (problems with timestamp otherwise) */
1344 /*ICSInitScript();*/
1345 if (appData.icsInputBox) ICSInputBoxPopUp();
1349 signal(SIGWINCH, TermSizeSigHandler);
1351 signal(SIGINT, IntSigHandler);
1352 signal(SIGTERM, IntSigHandler);
1353 if (*appData.cmailGameName != NULLCHAR) {
1354 signal(SIGUSR1, CmailSigHandler);
1359 // XtSetKeyboardFocus(shellWidget, formWidget);
1360 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1362 XtAppMainLoop(appContext);
1363 if (appData.debugMode) fclose(debugFP); // [DM] debug
1371 while((m = XtAppPending(appContext))) XtAppProcessEvent(appContext, m);
1375 TermSizeSigHandler (int sig)
1381 IntSigHandler (int sig)
1387 CmailSigHandler (int sig)
1392 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1394 /* Activate call-back function CmailSigHandlerCallBack() */
1395 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1397 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1401 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1404 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1406 /**** end signal code ****/
1409 #define Abs(n) ((n)<0 ? -(n) : (n))
1413 InsertPxlSize (char *pattern, int targetPxlSize)
1415 char *base_fnt_lst, strInt[12], *p, *q;
1416 int alternatives, i, len, strIntLen;
1419 * Replace the "*" (if present) in the pixel-size slot of each
1420 * alternative with the targetPxlSize.
1424 while ((p = strchr(p, ',')) != NULL) {
1428 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1429 strIntLen = strlen(strInt);
1430 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1434 while (alternatives--) {
1435 char *comma = strchr(p, ',');
1436 for (i=0; i<14; i++) {
1437 char *hyphen = strchr(p, '-');
1439 if (comma && hyphen > comma) break;
1440 len = hyphen + 1 - p;
1441 if (i == 7 && *p == '*' && len == 2) {
1443 memcpy(q, strInt, strIntLen);
1453 len = comma + 1 - p;
1460 return base_fnt_lst;
1464 CreateFontSet (char *base_fnt_lst)
1467 char **missing_list;
1471 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1472 &missing_list, &missing_count, &def_string);
1473 if (appData.debugMode) {
1475 XFontStruct **font_struct_list;
1476 char **font_name_list;
1477 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1479 fprintf(debugFP, " got list %s, locale %s\n",
1480 XBaseFontNameListOfFontSet(fntSet),
1481 XLocaleOfFontSet(fntSet));
1482 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1483 for (i = 0; i < count; i++) {
1484 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1487 for (i = 0; i < missing_count; i++) {
1488 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1491 if (fntSet == NULL) {
1492 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1497 #else // not ENABLE_NLS
1499 * Find a font that matches "pattern" that is as close as
1500 * possible to the targetPxlSize. Prefer fonts that are k
1501 * pixels smaller to fonts that are k pixels larger. The
1502 * pattern must be in the X Consortium standard format,
1503 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1504 * The return value should be freed with XtFree when no
1508 FindFont (char *pattern, int targetPxlSize)
1510 char **fonts, *p, *best, *scalable, *scalableTail;
1511 int i, j, nfonts, minerr, err, pxlSize;
1513 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1515 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1516 programName, pattern);
1523 for (i=0; i<nfonts; i++) {
1526 if (*p != '-') continue;
1528 if (*p == NULLCHAR) break;
1529 if (*p++ == '-') j++;
1531 if (j < 7) continue;
1534 scalable = fonts[i];
1537 err = pxlSize - targetPxlSize;
1538 if (Abs(err) < Abs(minerr) ||
1539 (minerr > 0 && err < 0 && -err == minerr)) {
1545 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1546 /* If the error is too big and there is a scalable font,
1547 use the scalable font. */
1548 int headlen = scalableTail - scalable;
1549 p = (char *) XtMalloc(strlen(scalable) + 10);
1550 while (isdigit(*scalableTail)) scalableTail++;
1551 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1553 p = (char *) XtMalloc(strlen(best) + 2);
1554 safeStrCpy(p, best, strlen(best)+1 );
1556 if (appData.debugMode) {
1557 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1558 pattern, targetPxlSize, p);
1560 XFreeFontNames(fonts);
1566 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1569 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1575 MarkMenuItem (char *menuRef, int state)
1577 MenuItem *item = MenuNameToItem(menuRef);
1581 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1582 XtSetValues(item->handle, args, 1);
1587 EnableNamedMenuItem (char *menuRef, int state)
1589 MenuItem *item = MenuNameToItem(menuRef);
1591 if(item) XtSetSensitive(item->handle, state);
1595 EnableButtonBar (int state)
1597 XtSetSensitive(optList[W_BUTTON].handle, state);
1602 SetMenuEnables (Enables *enab)
1604 while (enab->name != NULL) {
1605 EnableNamedMenuItem(enab->name, enab->value);
1611 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1612 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1614 if(*nprms == 0) return;
1615 item = MenuNameToItem(prms[0]);
1616 if(item) ((MenuProc *) item->proc) ();
1628 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1629 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1630 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1631 dmEnables[i].piece);
1632 XtSetSensitive(entry, p != NULL || !appData.testLegality
1633 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1634 && !appData.icsActive));
1636 while (p && *p++ == dmEnables[i].piece) count++;
1637 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1639 XtSetArg(args[j], XtNlabel, label); j++;
1640 XtSetValues(entry, args, j);
1645 do_flash_delay (unsigned long msec)
1651 FlashDelay (int flash_delay)
1653 XSync(xDisplay, False);
1654 if(flash_delay) do_flash_delay(flash_delay);
1658 Fraction (int x, int start, int stop)
1660 double f = ((double) x - start)/(stop - start);
1661 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1665 static WindowPlacement wpNew;
1668 CoDrag (Widget sh, WindowPlacement *wp)
1671 int j=0, touch=0, fudge = 2;
1672 GetActualPlacement(sh, wp);
1673 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1674 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1675 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1676 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1677 if(!touch ) return; // only windows that touch co-move
1678 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1679 int heightInc = wpNew.height - wpMain.height;
1680 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1681 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1682 wp->y += fracTop * heightInc;
1683 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1684 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1685 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1686 int widthInc = wpNew.width - wpMain.width;
1687 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1688 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1689 wp->y += fracLeft * widthInc;
1690 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1691 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1693 wp->x += wpNew.x - wpMain.x;
1694 wp->y += wpNew.y - wpMain.y;
1695 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1696 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1697 XtSetArg(args[j], XtNx, wp->x); j++;
1698 XtSetArg(args[j], XtNy, wp->y); j++;
1699 XtSetValues(sh, args, j);
1703 ReSize (WindowPlacement *wp)
1706 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1707 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1708 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1709 if(sqy < sqx) sqx = sqy;
1710 if(sqx != squareSize) {
1711 squareSize = sqx; // adopt new square size
1712 CreatePNGPieces(); // make newly scaled pieces
1713 InitDrawingSizes(0, 0); // creates grid etc.
1714 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1715 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1716 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1717 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1718 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1721 static XtIntervalId delayedDragID = 0;
1730 GetActualPlacement(shellWidget, &wpNew);
1731 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1732 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1733 busy = 0; return; // false alarm
1736 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1737 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1738 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1739 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1741 DrawPosition(True, NULL);
1742 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1750 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1752 XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1756 EventProc (Widget widget, caddr_t unused, XEvent *event)
1758 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1759 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1763 * event handler for redrawing the board
1766 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1768 DrawPosition(True, NULL);
1773 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1774 { // [HGM] pv: walk PV
1775 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1778 extern int savedIndex; /* gross that this is global */
1781 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1784 XawTextPosition index, dummy;
1787 XawTextGetSelectionPos(w, &index, &dummy);
1788 XtSetArg(arg, XtNstring, &val);
1789 XtGetValues(w, &arg, 1);
1790 ReplaceComment(savedIndex, val);
1791 if(savedIndex != currentMove) ToNrEvent(savedIndex);
1792 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1796 /* Disable all user input other than deleting the window */
1797 static int frozen = 0;
1803 /* Grab by a widget that doesn't accept input */
1804 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1808 /* Undo a FreezeUI */
1812 if (!frozen) return;
1813 XtRemoveGrab(optList[W_MESSG].handle);
1821 static int oldPausing = FALSE;
1822 static GameMode oldMode = (GameMode) -1;
1825 if (!boardWidget || !XtIsRealized(boardWidget)) return;
1827 if (pausing != oldPausing) {
1828 oldPausing = pausing;
1829 MarkMenuItem("Mode.Pause", pausing);
1831 if (appData.showButtonBar) {
1832 /* Always toggle, don't set. Previous code messes up when
1833 invoked while the button is pressed, as releasing it
1834 toggles the state again. */
1837 XtSetArg(args[0], XtNbackground, &oldbg);
1838 XtSetArg(args[1], XtNforeground, &oldfg);
1839 XtGetValues(optList[W_PAUSE].handle,
1841 XtSetArg(args[0], XtNbackground, oldfg);
1842 XtSetArg(args[1], XtNforeground, oldbg);
1844 XtSetValues(optList[W_PAUSE].handle, args, 2);
1848 wname = ModeToWidgetName(oldMode);
1849 if (wname != NULL) {
1850 MarkMenuItem(wname, False);
1852 wname = ModeToWidgetName(gameMode);
1853 if (wname != NULL) {
1854 MarkMenuItem(wname, True);
1856 if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
1857 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1860 /* Maybe all the enables should be handled here, not just this one */
1861 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1863 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1868 * Button/menu procedures
1871 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1872 char *selected_fen_position=NULL;
1875 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1876 Atom *type_return, XtPointer *value_return,
1877 unsigned long *length_return, int *format_return)
1879 char *selection_tmp;
1881 // if (!selected_fen_position) return False; /* should never happen */
1882 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1883 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1884 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1887 if (f == NULL) return False;
1891 selection_tmp = XtMalloc(len + 1);
1892 count = fread(selection_tmp, 1, len, f);
1895 XtFree(selection_tmp);
1898 selection_tmp[len] = NULLCHAR;
1900 /* note: since no XtSelectionDoneProc was registered, Xt will
1901 * automatically call XtFree on the value returned. So have to
1902 * make a copy of it allocated with XtMalloc */
1903 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1904 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1907 *value_return=selection_tmp;
1908 *length_return=strlen(selection_tmp);
1909 *type_return=*target;
1910 *format_return = 8; /* bits per byte */
1912 } else if (*target == XA_TARGETS(xDisplay)) {
1913 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1914 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1915 targets_tmp[1] = XA_STRING;
1916 *value_return = targets_tmp;
1917 *type_return = XA_ATOM;
1920 // This code leads to a read of value_return out of bounds on 64-bit systems.
1921 // Other code which I have seen always sets *format_return to 32 independent of
1922 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1923 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1924 *format_return = 8 * sizeof(Atom);
1925 if (*format_return > 32) {
1926 *length_return *= *format_return / 32;
1927 *format_return = 32;
1930 *format_return = 32;
1938 /* note: when called from menu all parameters are NULL, so no clue what the
1939 * Widget which was clicked on was, or what the click event was
1942 CopySomething (char *src)
1944 selected_fen_position = src;
1946 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
1947 * have a notion of a position that is selected but not copied.
1948 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
1950 XtOwnSelection(menuBarWidget, XA_PRIMARY,
1952 SendPositionSelection,
1953 NULL/* lose_ownership_proc */ ,
1954 NULL/* transfer_done_proc */);
1955 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
1957 SendPositionSelection,
1958 NULL/* lose_ownership_proc */ ,
1959 NULL/* transfer_done_proc */);
1962 /* function called when the data to Paste is ready */
1964 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
1965 Atom *type, XtPointer value, unsigned long *len, int *format)
1968 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
1969 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
1970 EditPositionPasteFEN(fenstr);
1974 /* called when Paste Position button is pressed,
1975 * all parameters will be NULL */
1977 PastePositionProc ()
1979 XtGetSelectionValue(menuBarWidget,
1980 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
1981 /* (XtSelectionCallbackProc) */ PastePositionCB,
1982 NULL, /* client_data passed to PastePositionCB */
1984 /* better to use the time field from the event that triggered the
1985 * call to this function, but that isn't trivial to get
1992 /* note: when called from menu all parameters are NULL, so no clue what the
1993 * Widget which was clicked on was, or what the click event was
1995 /* function called when the data to Paste is ready */
1997 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
1998 Atom *type, XtPointer value, unsigned long *len, int *format)
2001 int flip = appData.flipView;
2002 if (value == NULL || *len == 0) {
2003 return; /* nothing had been selected to copy */
2005 f = fopen(gamePasteFilename, "w");
2007 DisplayError(_("Can't open temp file"), errno);
2010 fwrite(value, 1, *len, f);
2013 if(!appData.autoFlipView) appData.flipView = flipView;
2014 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2015 appData.flipView = flip;
2018 /* called when Paste Game button is pressed,
2019 * all parameters will be NULL */
2023 XtGetSelectionValue(menuBarWidget,
2024 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2025 /* (XtSelectionCallbackProc) */ PasteGameCB,
2026 NULL, /* client_data passed to PasteGameCB */
2028 /* better to use the time field from the event that triggered the
2029 * call to this function, but that isn't trivial to get
2038 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2045 { // bassic primitive for determining if modifier keys are pressed
2046 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2049 XQueryKeymap(xDisplay,keys);
2050 for(i=0; i<6; i++) {
2052 j = XKeysymToKeycode(xDisplay, codes[i]);
2053 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2059 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2063 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2064 if ( n == 1 && *buf >= 32 // printable
2065 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2066 ) BoxAutoPopUp (buf);
2070 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2071 { // [HGM] input: let up-arrow recall previous line from history
2076 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2077 { // [HGM] input: let down-arrow recall next line from history
2082 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2088 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2090 if (!TempBackwardActive) {
2091 TempBackwardActive = True;
2097 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2099 /* Check to see if triggered by a key release event for a repeating key.
2100 * If so the next queued event will be a key press of the same key at the same time */
2101 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2103 XPeekEvent(xDisplay, &next);
2104 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2105 next.xkey.keycode == event->xkey.keycode)
2109 TempBackwardActive = False;
2113 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2114 { // called as key binding
2117 if (nprms && *nprms > 0)
2121 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2127 { // called from menu
2128 ManInner(NULL, NULL, NULL, NULL);
2135 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
2141 SetWindowTitle (char *text, char *title, char *icon)
2145 if (appData.titleInWindow) {
2147 XtSetArg(args[i], XtNlabel, text); i++;
2148 XtSetValues(titleWidget, args, i);
2151 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2152 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2153 XtSetValues(shellWidget, args, i);
2154 XSync(xDisplay, False);
2159 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2165 DisplayIcsInteractionTitle (String message)
2167 if (oldICSInteractionTitle == NULL) {
2168 /* Magic to find the old window title, adapted from vim */
2169 char *wina = getenv("WINDOWID");
2171 Window win = (Window) atoi(wina);
2172 Window root, parent, *children;
2173 unsigned int nchildren;
2174 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2176 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2177 if (!XQueryTree(xDisplay, win, &root, &parent,
2178 &children, &nchildren)) break;
2179 if (children) XFree((void *)children);
2180 if (parent == root || parent == 0) break;
2183 XSetErrorHandler(oldHandler);
2185 if (oldICSInteractionTitle == NULL) {
2186 oldICSInteractionTitle = "xterm";
2189 printf("\033]0;%s\007", message);
2194 XtIntervalId delayedEventTimerXID = 0;
2195 DelayedEventCallback delayedEventCallback = 0;
2200 delayedEventTimerXID = 0;
2201 delayedEventCallback();
2205 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2207 if(delayedEventTimerXID && delayedEventCallback == cb)
2208 // [HGM] alive: replace, rather than add or flush identical event
2209 XtRemoveTimeOut(delayedEventTimerXID);
2210 delayedEventCallback = cb;
2211 delayedEventTimerXID =
2212 XtAppAddTimeOut(appContext, millisec,
2213 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2216 DelayedEventCallback
2219 if (delayedEventTimerXID) {
2220 return delayedEventCallback;
2227 CancelDelayedEvent ()
2229 if (delayedEventTimerXID) {
2230 XtRemoveTimeOut(delayedEventTimerXID);
2231 delayedEventTimerXID = 0;
2235 XtIntervalId loadGameTimerXID = 0;
2238 LoadGameTimerRunning ()
2240 return loadGameTimerXID != 0;
2244 StopLoadGameTimer ()
2246 if (loadGameTimerXID != 0) {
2247 XtRemoveTimeOut(loadGameTimerXID);
2248 loadGameTimerXID = 0;
2256 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2258 loadGameTimerXID = 0;
2263 StartLoadGameTimer (long millisec)
2266 XtAppAddTimeOut(appContext, millisec,
2267 (XtTimerCallbackProc) LoadGameTimerCallback,
2271 XtIntervalId analysisClockXID = 0;
2274 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2276 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2277 || appData.icsEngineAnalyze) { // [DM]
2278 AnalysisPeriodicEvent(0);
2279 StartAnalysisClock();
2284 StartAnalysisClock ()
2287 XtAppAddTimeOut(appContext, 2000,
2288 (XtTimerCallbackProc) AnalysisClockCallback,
2292 XtIntervalId clockTimerXID = 0;
2295 ClockTimerRunning ()
2297 return clockTimerXID != 0;
2303 if (clockTimerXID != 0) {
2304 XtRemoveTimeOut(clockTimerXID);
2313 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2320 StartClockTimer (long millisec)
2323 XtAppAddTimeOut(appContext, millisec,
2324 (XtTimerCallbackProc) ClockTimerCallback,
2329 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2333 Widget w = (Widget) opt->handle;
2335 /* check for low time warning */
2336 Pixel foregroundOrWarningColor = timerForegroundPixel;
2339 appData.lowTimeWarning &&
2340 (timer / 1000) < appData.icsAlarmTime)
2341 foregroundOrWarningColor = lowTimeWarningColor;
2343 if (appData.clockMode) {
2344 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2345 XtSetArg(args[0], XtNlabel, buf);
2347 snprintf(buf, MSG_SIZ, "%s ", color);
2348 XtSetArg(args[0], XtNlabel, buf);
2353 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2354 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2356 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2357 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2360 XtSetValues(w, args, 3);
2363 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2366 SetClockIcon (int color)
2369 Pixmap pm = *clockIcons[color];
2370 if (iconPixmap != pm) {
2372 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2373 XtSetValues(shellWidget, args, 1);
2377 #define INPUT_SOURCE_BUF_SIZE 8192
2386 char buf[INPUT_SOURCE_BUF_SIZE];
2391 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2393 InputSource *is = (InputSource *) closure;
2398 if (is->lineByLine) {
2399 count = read(is->fd, is->unused,
2400 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2402 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2405 is->unused += count;
2407 while (p < is->unused) {
2408 q = memchr(p, '\n', is->unused - p);
2409 if (q == NULL) break;
2411 (is->func)(is, is->closure, p, q - p, 0);
2415 while (p < is->unused) {
2420 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2425 (is->func)(is, is->closure, is->buf, count, error);
2430 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2433 ChildProc *cp = (ChildProc *) pr;
2435 is = (InputSource *) calloc(1, sizeof(InputSource));
2436 is->lineByLine = lineByLine;
2440 is->fd = fileno(stdin);
2442 is->kind = cp->kind;
2443 is->fd = cp->fdFrom;
2446 is->unused = is->buf;
2449 is->xid = XtAppAddInput(appContext, is->fd,
2450 (XtPointer) (XtInputReadMask),
2451 (XtInputCallbackProc) DoInputCallback,
2453 is->closure = closure;
2454 return (InputSourceRef) is;
2458 RemoveInputSource (InputSourceRef isr)
2460 InputSource *is = (InputSource *) isr;
2462 if (is->xid == 0) return;
2463 XtRemoveInput(is->xid);
2469 static Boolean frameWaiting;
2472 FrameAlarm (int sig)
2474 frameWaiting = False;
2475 /* In case System-V style signals. Needed?? */
2476 signal(SIGALRM, FrameAlarm);
2480 FrameDelay (int time)
2482 struct itimerval delay;
2484 XSync(xDisplay, False);
2487 frameWaiting = True;
2488 signal(SIGALRM, FrameAlarm);
2489 delay.it_interval.tv_sec =
2490 delay.it_value.tv_sec = time / 1000;
2491 delay.it_interval.tv_usec =
2492 delay.it_value.tv_usec = (time % 1000) * 1000;
2493 setitimer(ITIMER_REAL, &delay, NULL);
2494 while (frameWaiting) pause();
2495 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2496 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2497 setitimer(ITIMER_REAL, &delay, NULL);
2504 FrameDelay (int time)
2506 XSync(xDisplay, False);
2508 usleep(time * 1000);
2514 FindLogo (char *place, char *name, char *buf)
2515 { // check if file exists in given place
2517 if(!place) return 0;
2518 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2519 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2527 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2529 char buf[MSG_SIZ], *logoName = buf;
2530 if(appData.logo[n][0]) {
2531 logoName = appData.logo[n];
2532 } else if(appData.autoLogo) {
2533 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2534 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2535 } else { // engine; cascade
2536 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2537 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2538 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2539 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2543 { ASSIGN(cps->programLogo, logoName); }
2547 UpdateLogos (int displ)
2549 if(optList[W_WHITE-1].handle == NULL) return;
2550 LoadLogo(&first, 0, 0);
2551 LoadLogo(&second, 1, appData.icsActive);
2552 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);