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 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.
188 #define IMAGE_EXT "xpm"
190 #define IMAGE_EXT "xim"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
208 #include "engineoutput.h"
218 #define usleep(t) _sleep2(((t)+500)/1000)
222 # define _(s) gettext (s)
223 # define N_(s) gettext_noop (s)
229 int main P((int argc, char **argv));
230 RETSIGTYPE CmailSigHandler P((int sig));
231 RETSIGTYPE IntSigHandler P((int sig));
232 RETSIGTYPE TermSizeSigHandler P((int sig));
233 Widget CreateMenuBar P((Menu *mb, int boardWidth));
235 char *InsertPxlSize P((char *pattern, int targetPxlSize));
236 XFontSet CreateFontSet P((char *base_fnt_lst));
238 char *FindFont P((char *pattern, int targetPxlSize));
240 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
241 u_int wreq, u_int hreq));
242 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
243 void DelayedDrag P((void));
244 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
245 void HandlePV P((Widget w, XEvent * event,
246 String * params, Cardinal * nParams));
247 void DrawPositionProc P((Widget w, XEvent *event,
248 String *prms, Cardinal *nprms));
249 void CommentClick P((Widget w, XEvent * event,
250 String * params, Cardinal * nParams));
251 void ICSInputBoxPopUp P((void));
252 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
253 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
254 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
255 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
256 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
257 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
258 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
259 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
260 Boolean TempBackwardActive = False;
261 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
262 void DisplayMove P((int moveNumber));
263 void update_ics_width P(());
264 int CopyMemoProc P(());
267 * XBoard depends on Xt R4 or higher
269 int xtVersion = XtSpecificationRelease;
274 Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
275 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
276 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
277 Option *optList; // contains all widgets of main window
279 XFontSet fontSet, clockFontSet;
282 XFontStruct *clockFontStruct;
284 Font coordFontID, countFontID;
285 XFontStruct *coordFontStruct, *countFontStruct;
286 XtAppContext appContext;
289 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
291 Position commentX = -1, commentY = -1;
292 Dimension commentW, commentH;
293 typedef unsigned int BoardSize;
295 Boolean chessProgram;
297 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
298 int smallLayout = 0, tinyLayout = 0,
299 marginW, marginH, // [HGM] for run-time resizing
300 fromX = -1, fromY = -1, toX, toY, commentUp = False,
301 errorExitStatus = -1, defaultLineGap;
302 Dimension textHeight;
303 Pixel timerForegroundPixel, timerBackgroundPixel;
304 Pixel buttonForegroundPixel, buttonBackgroundPixel;
305 char *chessDir, *programName, *programVersion;
306 Boolean alwaysOnTop = False;
307 char *icsTextMenuString;
309 char *firstChessProgramNames;
310 char *secondChessProgramNames;
312 WindowPlacement wpMain;
313 WindowPlacement wpConsole;
314 WindowPlacement wpComment;
315 WindowPlacement wpMoveHistory;
316 WindowPlacement wpEvalGraph;
317 WindowPlacement wpEngineOutput;
318 WindowPlacement wpGameList;
319 WindowPlacement wpTags;
320 WindowPlacement wpDualBoard;
323 /* This magic number is the number of intermediate frames used
324 in each half of the animation. For short moves it's reduced
325 by 1. The total number of frames will be factor * 2 + 1. */
328 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
335 DropMenuEnables dmEnables[] = {
352 XtResource clientResources[] = {
353 { "flashCount", "flashCount", XtRInt, sizeof(int),
354 XtOffset(AppDataPtr, flashCount), XtRImmediate,
355 (XtPointer) FLASH_COUNT },
358 XrmOptionDescRec shellOptions[] = {
359 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
360 { "-flash", "flashCount", XrmoptionNoArg, "3" },
361 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
364 XtActionsRec boardActions[] = {
365 { "DrawPosition", DrawPositionProc },
366 { "HandlePV", HandlePV },
367 { "SelectPV", SelectPV },
368 { "StopPV", StopPV },
369 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
370 { "QuitProc", QuitWrapper },
371 { "ManProc", ManInner },
372 { "TempBackwardProc", TempBackwardProc },
373 { "TempForwardProc", TempForwardProc },
374 { "CommentClick", (XtActionProc) CommentClick },
375 { "GenericPopDown", (XtActionProc) GenericPopDown },
376 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
377 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
378 { "SelectMove", (XtActionProc) SelectMoveX },
379 { "LoadSelectedProc", LoadSelectedProc },
380 { "SetFilterProc", SetFilterProc },
381 { "TypeInProc", TypeInProc },
382 { "EnterKeyProc", EnterKeyProc },
383 { "UpKeyProc", UpKeyProc },
384 { "DownKeyProc", DownKeyProc },
385 { "WheelProc", WheelProc },
386 { "TabProc", TabProc },
389 char globalTranslations[] =
390 ":Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
391 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
392 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
393 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
394 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
395 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
396 :<Key>Pause: MenuItem(Mode.Pause) \n \
397 :Ctrl<Key>d: MenuItem(DebugProc) \n \
398 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
399 :<Key>Left: MenuItem(Edit.Backward) \n \
400 :<Key>Right: MenuItem(Edit.Forward) \n \
401 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
402 #ifndef OPTIONSDIALOG
404 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
405 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
406 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
407 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
408 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
411 :<KeyDown>Return: TempBackwardProc() \n \
412 :<KeyUp>Return: TempForwardProc() \n";
414 char ICSInputTranslations[] =
415 "<Key>Up: UpKeyProc() \n "
416 "<Key>Down: DownKeyProc() \n "
417 "<Key>Return: EnterKeyProc() \n";
419 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
420 // as the widget is destroyed before the up-click can call extend-end
421 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
423 String xboardResources[] = {
424 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
429 /* Max possible square size */
430 #define MAXSQSIZE 256
432 static int xpm_avail[MAXSQSIZE];
434 #ifdef HAVE_DIR_STRUCT
436 /* Extract piece size from filename */
438 xpm_getsize (char *name, int len, char *ext)
446 if ((p=strchr(name, '.')) == NULL ||
447 StrCaseCmp(p+1, ext) != 0)
453 while (*p && isdigit(*p))
460 /* Setup xpm_avail */
462 xpm_getavail (char *dirname, char *ext)
468 for (i=0; i<MAXSQSIZE; ++i)
471 if (appData.debugMode)
472 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
474 dir = opendir(dirname);
477 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
478 programName, dirname);
482 while ((ent=readdir(dir)) != NULL) {
483 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
484 if (i > 0 && i < MAXSQSIZE)
494 xpm_print_avail (FILE *fp, char *ext)
498 fprintf(fp, _("Available `%s' sizes:\n"), ext);
499 for (i=1; i<MAXSQSIZE; ++i) {
505 /* Return XPM piecesize closest to size */
507 xpm_closest_to (char *dirname, int size, char *ext)
510 int sm_diff = MAXSQSIZE;
514 xpm_getavail(dirname, ext);
516 if (appData.debugMode)
517 xpm_print_avail(stderr, ext);
519 for (i=1; i<MAXSQSIZE; ++i) {
522 diff = (diff<0) ? -diff : diff;
523 if (diff < sm_diff) {
531 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
537 #else /* !HAVE_DIR_STRUCT */
538 /* If we are on a system without a DIR struct, we can't
539 read the directory, so we can't collect a list of
540 filenames, etc., so we can't do any size-fitting. */
542 xpm_closest_to (char *dirname, int size, char *ext)
545 Warning: No DIR structure found on this system --\n\
546 Unable to autosize for XPM/XIM pieces.\n\
547 Please report this error to %s.\n\
548 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
551 #endif /* HAVE_DIR_STRUCT */
554 /* Arrange to catch delete-window events */
555 Atom wm_delete_window;
557 CatchDeleteWindow (Widget w, String procname)
560 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
561 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
562 XtAugmentTranslations(w, XtParseTranslationTable(buf));
569 XtSetArg(args[0], XtNiconic, False);
570 XtSetValues(shellWidget, args, 1);
572 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
575 //---------------------------------------------------------------------------------------------------------
576 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
579 #define CW_USEDEFAULT (1<<31)
580 #define ICS_TEXT_MENU_SIZE 90
581 #define DEBUG_FILE "xboard.debug"
582 #define SetCurrentDirectory chdir
583 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
587 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
590 // front-end part of option handling
592 // [HGM] This platform-dependent table provides the location for storing the color info
593 extern char *crWhite, * crBlack;
597 &appData.whitePieceColor,
598 &appData.blackPieceColor,
599 &appData.lightSquareColor,
600 &appData.darkSquareColor,
601 &appData.highlightSquareColor,
602 &appData.premoveHighlightColor,
603 &appData.lowTimeWarningColor,
614 // [HGM] font: keep a font for each square size, even non-stndard ones
617 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
618 char *fontTable[NUM_FONTS][MAX_SIZE];
621 ParseFont (char *name, int number)
622 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
624 if(sscanf(name, "size%d:", &size)) {
625 // [HGM] font: font is meant for specific boardSize (likely from settings file);
626 // defer processing it until we know if it matches our board size
627 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
628 fontTable[number][size] = strdup(strchr(name, ':')+1);
629 fontValid[number][size] = True;
634 case 0: // CLOCK_FONT
635 appData.clockFont = strdup(name);
637 case 1: // MESSAGE_FONT
638 appData.font = strdup(name);
640 case 2: // COORD_FONT
641 appData.coordFont = strdup(name);
646 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
651 { // only 2 fonts currently
652 appData.clockFont = CLOCK_FONT_NAME;
653 appData.coordFont = COORD_FONT_NAME;
654 appData.font = DEFAULT_FONT_NAME;
659 { // no-op, until we identify the code for this already in XBoard and move it here
663 ParseColor (int n, char *name)
664 { // in XBoard, just copy the color-name string
665 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
669 ParseTextAttribs (ColorClass cc, char *s)
671 (&appData.colorShout)[cc] = strdup(s);
675 ParseBoardSize (void *addr, char *name)
677 appData.boardSize = strdup(name);
682 { // In XBoard the sound-playing program takes care of obtaining the actual sound
686 SetCommPortDefaults ()
687 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
690 // [HGM] args: these three cases taken out to stay in front-end
692 SaveFontArg (FILE *f, ArgDescriptor *ad)
695 int i, n = (int)(intptr_t)ad->argLoc;
697 case 0: // CLOCK_FONT
698 name = appData.clockFont;
700 case 1: // MESSAGE_FONT
703 case 2: // COORD_FONT
704 name = appData.coordFont;
709 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
710 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
711 fontTable[n][squareSize] = strdup(name);
712 fontValid[n][squareSize] = True;
715 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
716 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
721 { // nothing to do, as the sounds are at all times represented by their text-string names already
725 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
726 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
727 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
731 SaveColor (FILE *f, ArgDescriptor *ad)
732 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
733 if(colorVariable[(int)(intptr_t)ad->argLoc])
734 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
738 SaveBoardSize (FILE *f, char *name, void *addr)
739 { // wrapper to shield back-end from BoardSize & sizeInfo
740 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
744 ParseCommPortSettings (char *s)
745 { // no such option in XBoard (yet)
751 GetActualPlacement (Widget wg, WindowPlacement *wp)
753 XWindowAttributes winAt;
760 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
761 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
762 wp->x = rx - winAt.x;
763 wp->y = ry - winAt.y;
764 wp->height = winAt.height;
765 wp->width = winAt.width;
766 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
771 { // wrapper to shield use of window handles from back-end (make addressible by number?)
772 // In XBoard this will have to wait until awareness of window parameters is implemented
773 GetActualPlacement(shellWidget, &wpMain);
774 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
775 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
776 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
777 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
778 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
779 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
783 PrintCommPortSettings (FILE *f, char *name)
784 { // This option does not exist in XBoard
788 EnsureOnScreen (int *x, int *y, int minX, int minY)
795 { // [HGM] args: allows testing if main window is realized from back-end
796 return xBoardWindow != 0;
800 PopUpStartupDialog ()
801 { // start menu not implemented in XBoard
805 ConvertToLine (int argc, char **argv)
807 static char line[128*1024], buf[1024];
811 for(i=1; i<argc; i++)
813 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
814 && argv[i][0] != '{' )
815 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
817 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
818 strncat(line, buf, 128*1024 - strlen(line) - 1 );
821 line[strlen(line)-1] = NULLCHAR;
825 //--------------------------------------------------------------------------------------------
828 ResizeBoardWindow (int w, int h, int inhibit)
830 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
832 shellArgs[0].value = w;
833 shellArgs[1].value = h;
834 shellArgs[4].value = shellArgs[2].value = w;
835 shellArgs[5].value = shellArgs[3].value = h;
836 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
838 XSync(xDisplay, False);
842 MakeOneColor (char *name, Pixel *color)
845 if (!appData.monoMode) {
846 vFrom.addr = (caddr_t) name;
847 vFrom.size = strlen(name);
848 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
849 if (vTo.addr == NULL) {
850 appData.monoMode = True;
853 *color = *(Pixel *) vTo.addr;
861 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
862 int forceMono = False;
864 if (appData.lowTimeWarning)
865 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
866 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
867 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
873 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
874 { // detervtomine what fonts to use, and create them
878 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
879 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
880 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
881 appData.font = fontTable[MESSAGE_FONT][squareSize];
882 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
883 appData.coordFont = fontTable[COORD_FONT][squareSize];
886 appData.font = InsertPxlSize(appData.font, fontPxlSize);
887 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
888 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
889 fontSet = CreateFontSet(appData.font);
890 clockFontSet = CreateFontSet(appData.clockFont);
892 /* For the coordFont, use the 0th font of the fontset. */
893 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
894 XFontStruct **font_struct_list;
895 XFontSetExtents *fontSize;
896 char **font_name_list;
897 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
898 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
899 coordFontStruct = XQueryFont(xDisplay, coordFontID);
900 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
901 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
904 appData.font = FindFont(appData.font, fontPxlSize);
905 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
906 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
907 clockFontID = XLoadFont(xDisplay, appData.clockFont);
908 clockFontStruct = XQueryFont(xDisplay, clockFontID);
909 coordFontID = XLoadFont(xDisplay, appData.coordFont);
910 coordFontStruct = XQueryFont(xDisplay, coordFontID);
911 // textHeight in !NLS mode!
913 countFontID = coordFontID; // [HGM] holdings
914 countFontStruct = coordFontStruct;
916 xdb = XtDatabase(xDisplay);
918 XrmPutLineResource(&xdb, "*international: True");
919 vTo.size = sizeof(XFontSet);
920 vTo.addr = (XtPointer) &fontSet;
921 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
923 XrmPutStringResource(&xdb, "*font", appData.font);
933 case ArgInt: p = " N"; break;
934 case ArgString: p = " STR"; break;
935 case ArgBoolean: p = " TF"; break;
936 case ArgSettingsFilename:
937 case ArgFilename: p = " FILE"; break;
938 case ArgX: p = " Nx"; break;
939 case ArgY: p = " Ny"; break;
940 case ArgAttribs: p = " TEXTCOL"; break;
941 case ArgColor: p = " COL"; break;
942 case ArgFont: p = " FONT"; break;
943 case ArgBoardSize: p = " SIZE"; break;
944 case ArgFloat: p = " FLOAT"; break;
949 case ArgCommSettings:
956 GenerateGlobalTranslationTable (void)
958 /* go through all menu items and extract the keyboard shortcuts, so that X11 can load them */
966 /* loop over all menu entries */
967 for( i=0; menuBar[i].mi ; i++)
970 for(j=0; mi[j].proc; j++)
978 char *key,*test, *mods;
980 /* check for Ctrl/Alt */
981 if( strstr(mi[j].accel, "<Ctrl>") ) ctrl = 1;
982 if( strstr(mi[j].accel, "<Shift>") ) shift = 1;
983 if( strstr(mi[j].accel, "<Alt>") ) alt = 1;
985 /* remove all <...> */
986 test = strrchr(mi[j].accel, '>');
988 key = strdup(mi[j].accel);
990 key = strdup(++test); // remove ">"
992 /* instead of shift X11 uses the uppercase letter directly*/
993 if (shift && strlen(key)==1 )
995 *key = toupper(*key);
999 /* handle some special cases which have different names in X11 */
1000 if ( strncmp(key, "Page_Down", 9) == 0 )
1005 else if ( strncmp(key, "Page_Up", 7) == 0 )
1008 key=strdup("Prior");
1011 /* create string of mods */
1013 mods = strdup("Ctrl ");
1019 mods = realloc(mods, strlen(mods) + strlen("Meta ")+1);
1020 strncat(mods, "Meta ", 5);
1025 mods = realloc(mods, strlen(mods) + strlen("Shift ")+1);
1026 strncat(mods, "Shift ", 6);
1029 // remove trailing space
1030 if( isspace(mods[strlen(mods)-1]) )
1031 mods[strlen(mods)-1]='\0';
1033 /* get the name for the callback, we can use MenuItem() here that will call KeyBindingProc */
1034 size_t namesize = snprintf(NULL, 0, "%s.%s", menuBar[i].ref, mi[j].ref);
1035 char *name = malloc(namesize+1);
1036 snprintf(name, namesize+1, "%s.%s", menuBar[i].ref, mi[j].ref);
1038 size_t buffersize = snprintf(NULL, 0, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
1039 char *buffer = malloc(buffersize+1);
1040 snprintf(buffer, buffersize+1, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
1042 /* add string to the output */
1043 output = realloc(output, strlen(output) + strlen(buffer)+1);
1044 strncat(output, buffer, strlen(buffer));
1063 ArgDescriptor *q, *p = argDescriptors+5;
1064 printf("\nXBoard accepts the following options:\n"
1065 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1066 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1067 " SIZE = board-size spec(s)\n"
1068 " Within parentheses are short forms, or options to set to true or false.\n"
1069 " Persistent options (saved in the settings file) are marked with *)\n\n");
1071 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1072 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1073 if(p->save) strcat(buf+len, "*");
1074 for(q=p+1; q->argLoc == p->argLoc; q++) {
1075 if(q->argName[0] == '-') continue;
1076 strcat(buf+len, q == p+1 ? " (" : " ");
1077 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1079 if(q != p+1) strcat(buf+len, ")");
1081 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1084 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1088 main (int argc, char **argv)
1090 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1091 XSetWindowAttributes window_attributes;
1093 Dimension boardWidth, boardHeight, w, h;
1095 int forceMono = False;
1097 srandom(time(0)); // [HGM] book: make random truly random
1099 setbuf(stdout, NULL);
1100 setbuf(stderr, NULL);
1103 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1104 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1108 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1113 programName = strrchr(argv[0], '/');
1114 if (programName == NULL)
1115 programName = argv[0];
1120 XtSetLanguageProc(NULL, NULL, NULL);
1121 if (appData.debugMode) {
1122 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1125 bindtextdomain(PACKAGE, LOCALEDIR);
1126 textdomain(PACKAGE);
1129 appData.boardSize = "";
1130 InitAppData(ConvertToLine(argc, argv));
1132 if (p == NULL) p = "/tmp";
1133 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1134 gameCopyFilename = (char*) malloc(i);
1135 gamePasteFilename = (char*) malloc(i);
1136 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1137 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1139 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1140 static char buf[MSG_SIZ];
1141 EscapeExpand(buf, appData.firstInitString);
1142 appData.firstInitString = strdup(buf);
1143 EscapeExpand(buf, appData.secondInitString);
1144 appData.secondInitString = strdup(buf);
1145 EscapeExpand(buf, appData.firstComputerString);
1146 appData.firstComputerString = strdup(buf);
1147 EscapeExpand(buf, appData.secondComputerString);
1148 appData.secondComputerString = strdup(buf);
1151 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1154 if (chdir(chessDir) != 0) {
1155 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1161 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1162 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1163 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1164 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1167 setbuf(debugFP, NULL);
1170 /* [HGM,HR] make sure board size is acceptable */
1171 if(appData.NrFiles > BOARD_FILES ||
1172 appData.NrRanks > BOARD_RANKS )
1173 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1176 /* This feature does not work; animation needs a rewrite */
1177 appData.highlightDragging = FALSE;
1181 gameInfo.variant = StringToVariant(appData.variant);
1182 InitPosition(FALSE);
1185 XtAppInitialize(&appContext, "XBoard", shellOptions,
1186 XtNumber(shellOptions),
1187 &argc, argv, xboardResources, NULL, 0);
1189 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1190 clientResources, XtNumber(clientResources),
1193 xDisplay = XtDisplay(shellWidget);
1194 xScreen = DefaultScreen(xDisplay);
1195 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1198 * determine size, based on supplied or remembered -size, or screen size
1200 if (isdigit(appData.boardSize[0])) {
1201 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1202 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1203 &fontPxlSize, &smallLayout, &tinyLayout);
1205 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1206 programName, appData.boardSize);
1210 /* Find some defaults; use the nearest known size */
1211 SizeDefaults *szd, *nearest;
1212 int distance = 99999;
1213 nearest = szd = sizeDefaults;
1214 while (szd->name != NULL) {
1215 if (abs(szd->squareSize - squareSize) < distance) {
1217 distance = abs(szd->squareSize - squareSize);
1218 if (distance == 0) break;
1222 if (i < 2) lineGap = nearest->lineGap;
1223 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1224 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1225 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1226 if (i < 6) smallLayout = nearest->smallLayout;
1227 if (i < 7) tinyLayout = nearest->tinyLayout;
1230 SizeDefaults *szd = sizeDefaults;
1231 if (*appData.boardSize == NULLCHAR) {
1232 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1233 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1236 if (szd->name == NULL) szd--;
1237 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1239 while (szd->name != NULL &&
1240 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1241 if (szd->name == NULL) {
1242 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1243 programName, appData.boardSize);
1247 squareSize = szd->squareSize;
1248 lineGap = szd->lineGap;
1249 clockFontPxlSize = szd->clockFontPxlSize;
1250 coordFontPxlSize = szd->coordFontPxlSize;
1251 fontPxlSize = szd->fontPxlSize;
1252 smallLayout = szd->smallLayout;
1253 tinyLayout = szd->tinyLayout;
1254 // [HGM] font: use defaults from settings file if available and not overruled
1257 defaultLineGap = lineGap;
1258 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1260 /* [HR] height treated separately (hacked) */
1261 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1262 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1265 * Determine what fonts to use.
1267 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1270 * Detect if there are not enough colors available and adapt.
1272 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1273 appData.monoMode = True;
1276 forceMono = MakeColors();
1279 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1281 appData.monoMode = True;
1284 if (appData.monoMode && appData.debugMode) {
1285 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1286 (unsigned long) XWhitePixel(xDisplay, xScreen),
1287 (unsigned long) XBlackPixel(xDisplay, xScreen));
1290 ParseIcsTextColors();
1292 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1298 layoutName = "tinyLayout";
1299 } else if (smallLayout) {
1300 layoutName = "smallLayout";
1302 layoutName = "normalLayout";
1305 optList = BoardPopUp(squareSize, lineGap, (void*)
1311 InitDrawingHandle(optList + W_BOARD);
1312 currBoard = &optList[W_BOARD];
1313 boardWidget = optList[W_BOARD].handle;
1314 menuBarWidget = optList[W_MENU].handle;
1315 dropMenu = optList[W_DROP].handle;
1316 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1317 formWidget = XtParent(boardWidget);
1318 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1319 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1320 XtGetValues(optList[W_WHITE].handle, args, 2);
1321 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1322 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1323 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1324 XtGetValues(optList[W_PAUSE].handle, args, 2);
1327 xBoardWindow = XtWindow(boardWidget);
1329 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1330 // not need to go into InitDrawingSizes().
1333 * Create X checkmark bitmap and initialize option menu checks.
1335 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1336 checkmark_bits, checkmark_width, checkmark_height);
1342 ReadBitmap(&wIconPixmap, "icon_white.bm",
1343 icon_white_bits, icon_white_width, icon_white_height);
1344 ReadBitmap(&bIconPixmap, "icon_black.bm",
1345 icon_black_bits, icon_black_width, icon_black_height);
1346 iconPixmap = wIconPixmap;
1348 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1349 XtSetValues(shellWidget, args, i);
1352 * Create a cursor for the board widget.
1354 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1355 XChangeWindowAttributes(xDisplay, xBoardWindow,
1356 CWCursor, &window_attributes);
1359 * Inhibit shell resizing.
1361 shellArgs[0].value = (XtArgVal) &w;
1362 shellArgs[1].value = (XtArgVal) &h;
1363 XtGetValues(shellWidget, shellArgs, 2);
1364 shellArgs[4].value = shellArgs[2].value = w;
1365 shellArgs[5].value = shellArgs[3].value = h;
1366 // XtSetValues(shellWidget, &shellArgs[2], 4);
1367 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1368 marginH = h - boardHeight;
1370 CatchDeleteWindow(shellWidget, "QuitProc");
1375 if(appData.logoSize)
1376 { // locate and read user logo
1378 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1379 ASSIGN(userLogo, buf);
1382 if (appData.animate || appData.animateDragging)
1386 char *TranslationsTableMenus=GenerateGlobalTranslationTable ();
1388 XtAugmentTranslations(formWidget,
1389 XtParseTranslationTable(globalTranslations));
1390 XtAugmentTranslations(formWidget,
1391 XtParseTranslationTable(TranslationsTableMenus));
1393 XtAddEventHandler(formWidget, KeyPressMask, False,
1394 (XtEventHandler) MoveTypeInProc, NULL);
1395 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1396 (XtEventHandler) EventProc, NULL);
1398 /* [AS] Restore layout */
1399 if( wpMoveHistory.visible ) {
1403 if( wpEvalGraph.visible )
1408 if( wpEngineOutput.visible ) {
1409 EngineOutputPopUp();
1414 if (errorExitStatus == -1) {
1415 if (appData.icsActive) {
1416 /* We now wait until we see "login:" from the ICS before
1417 sending the logon script (problems with timestamp otherwise) */
1418 /*ICSInitScript();*/
1419 if (appData.icsInputBox) ICSInputBoxPopUp();
1423 signal(SIGWINCH, TermSizeSigHandler);
1425 signal(SIGINT, IntSigHandler);
1426 signal(SIGTERM, IntSigHandler);
1427 if (*appData.cmailGameName != NULLCHAR) {
1428 signal(SIGUSR1, CmailSigHandler);
1432 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1435 // XtSetKeyboardFocus(shellWidget, formWidget);
1436 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1438 XtAppMainLoop(appContext);
1439 if (appData.debugMode) fclose(debugFP); // [DM] debug
1444 TermSizeSigHandler (int sig)
1450 IntSigHandler (int sig)
1456 CmailSigHandler (int sig)
1461 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1463 /* Activate call-back function CmailSigHandlerCallBack() */
1464 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1466 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1470 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1473 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1475 /**** end signal code ****/
1478 #define Abs(n) ((n)<0 ? -(n) : (n))
1482 InsertPxlSize (char *pattern, int targetPxlSize)
1484 char *base_fnt_lst, strInt[12], *p, *q;
1485 int alternatives, i, len, strIntLen;
1488 * Replace the "*" (if present) in the pixel-size slot of each
1489 * alternative with the targetPxlSize.
1493 while ((p = strchr(p, ',')) != NULL) {
1497 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1498 strIntLen = strlen(strInt);
1499 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1503 while (alternatives--) {
1504 char *comma = strchr(p, ',');
1505 for (i=0; i<14; i++) {
1506 char *hyphen = strchr(p, '-');
1508 if (comma && hyphen > comma) break;
1509 len = hyphen + 1 - p;
1510 if (i == 7 && *p == '*' && len == 2) {
1512 memcpy(q, strInt, strIntLen);
1522 len = comma + 1 - p;
1529 return base_fnt_lst;
1533 CreateFontSet (char *base_fnt_lst)
1536 char **missing_list;
1540 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1541 &missing_list, &missing_count, &def_string);
1542 if (appData.debugMode) {
1544 XFontStruct **font_struct_list;
1545 char **font_name_list;
1546 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1548 fprintf(debugFP, " got list %s, locale %s\n",
1549 XBaseFontNameListOfFontSet(fntSet),
1550 XLocaleOfFontSet(fntSet));
1551 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1552 for (i = 0; i < count; i++) {
1553 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1556 for (i = 0; i < missing_count; i++) {
1557 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1560 if (fntSet == NULL) {
1561 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1566 #else // not ENABLE_NLS
1568 * Find a font that matches "pattern" that is as close as
1569 * possible to the targetPxlSize. Prefer fonts that are k
1570 * pixels smaller to fonts that are k pixels larger. The
1571 * pattern must be in the X Consortium standard format,
1572 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1573 * The return value should be freed with XtFree when no
1577 FindFont (char *pattern, int targetPxlSize)
1579 char **fonts, *p, *best, *scalable, *scalableTail;
1580 int i, j, nfonts, minerr, err, pxlSize;
1582 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1584 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1585 programName, pattern);
1592 for (i=0; i<nfonts; i++) {
1595 if (*p != '-') continue;
1597 if (*p == NULLCHAR) break;
1598 if (*p++ == '-') j++;
1600 if (j < 7) continue;
1603 scalable = fonts[i];
1606 err = pxlSize - targetPxlSize;
1607 if (Abs(err) < Abs(minerr) ||
1608 (minerr > 0 && err < 0 && -err == minerr)) {
1614 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1615 /* If the error is too big and there is a scalable font,
1616 use the scalable font. */
1617 int headlen = scalableTail - scalable;
1618 p = (char *) XtMalloc(strlen(scalable) + 10);
1619 while (isdigit(*scalableTail)) scalableTail++;
1620 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1622 p = (char *) XtMalloc(strlen(best) + 2);
1623 safeStrCpy(p, best, strlen(best)+1 );
1625 if (appData.debugMode) {
1626 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1627 pattern, targetPxlSize, p);
1629 XFreeFontNames(fonts);
1635 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1638 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1644 MarkMenuItem (char *menuRef, int state)
1646 MenuItem *item = MenuNameToItem(menuRef);
1650 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1651 XtSetValues(item->handle, args, 1);
1656 EnableNamedMenuItem (char *menuRef, int state)
1658 MenuItem *item = MenuNameToItem(menuRef);
1660 if(item) XtSetSensitive(item->handle, state);
1664 EnableButtonBar (int state)
1666 XtSetSensitive(optList[W_BUTTON].handle, state);
1671 SetMenuEnables (Enables *enab)
1673 while (enab->name != NULL) {
1674 EnableNamedMenuItem(enab->name, enab->value);
1680 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1681 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1683 if(*nprms == 0) return;
1684 item = MenuNameToItem(prms[0]);
1685 if(item) ((MenuProc *) item->proc) ();
1697 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1698 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1699 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1700 dmEnables[i].piece);
1701 XtSetSensitive(entry, p != NULL || !appData.testLegality
1702 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1703 && !appData.icsActive));
1705 while (p && *p++ == dmEnables[i].piece) count++;
1706 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1708 XtSetArg(args[j], XtNlabel, label); j++;
1709 XtSetValues(entry, args, j);
1714 do_flash_delay (unsigned long msec)
1720 FlashDelay (int flash_delay)
1722 XSync(xDisplay, False);
1723 if(flash_delay) do_flash_delay(flash_delay);
1727 Fraction (int x, int start, int stop)
1729 double f = ((double) x - start)/(stop - start);
1730 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1734 static WindowPlacement wpNew;
1737 CoDrag (Widget sh, WindowPlacement *wp)
1740 int j=0, touch=0, fudge = 2;
1741 GetActualPlacement(sh, wp);
1742 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1743 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1744 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1745 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1746 if(!touch ) return; // only windows that touch co-move
1747 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1748 int heightInc = wpNew.height - wpMain.height;
1749 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1750 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1751 wp->y += fracTop * heightInc;
1752 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1753 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1754 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1755 int widthInc = wpNew.width - wpMain.width;
1756 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1757 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1758 wp->y += fracLeft * widthInc;
1759 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1760 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1762 wp->x += wpNew.x - wpMain.x;
1763 wp->y += wpNew.y - wpMain.y;
1764 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1765 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1766 XtSetArg(args[j], XtNx, wp->x); j++;
1767 XtSetArg(args[j], XtNy, wp->y); j++;
1768 XtSetValues(sh, args, j);
1772 ReSize (WindowPlacement *wp)
1775 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1776 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1777 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1778 if(sqy < sqx) sqx = sqy;
1779 if(sqx != squareSize) {
1780 squareSize = sqx; // adopt new square size
1781 CreatePNGPieces(); // make newly scaled pieces
1782 InitDrawingSizes(0, 0); // creates grid etc.
1783 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1784 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1785 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1786 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1787 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1790 static XtIntervalId delayedDragID = 0;
1799 GetActualPlacement(shellWidget, &wpNew);
1800 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1801 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1802 busy = 0; return; // false alarm
1805 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1806 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1807 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1808 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1810 DrawPosition(True, NULL);
1811 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1819 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1821 XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1825 EventProc (Widget widget, caddr_t unused, XEvent *event)
1827 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1828 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1832 * event handler for redrawing the board
1835 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1837 DrawPosition(True, NULL);
1842 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1843 { // [HGM] pv: walk PV
1844 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1847 extern int savedIndex; /* gross that this is global */
1850 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1853 XawTextPosition index, dummy;
1856 XawTextGetSelectionPos(w, &index, &dummy);
1857 XtSetArg(arg, XtNstring, &val);
1858 XtGetValues(w, &arg, 1);
1859 ReplaceComment(savedIndex, val);
1860 if(savedIndex != currentMove) ToNrEvent(savedIndex);
1861 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1865 /* Disable all user input other than deleting the window */
1866 static int frozen = 0;
1872 /* Grab by a widget that doesn't accept input */
1873 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1877 /* Undo a FreezeUI */
1881 if (!frozen) return;
1882 XtRemoveGrab(optList[W_MESSG].handle);
1890 static int oldPausing = FALSE;
1891 static GameMode oldmode = (GameMode) -1;
1894 if (!boardWidget || !XtIsRealized(boardWidget)) return;
1896 if (pausing != oldPausing) {
1897 oldPausing = pausing;
1898 MarkMenuItem("Mode.Pause", pausing);
1900 if (appData.showButtonBar) {
1901 /* Always toggle, don't set. Previous code messes up when
1902 invoked while the button is pressed, as releasing it
1903 toggles the state again. */
1906 XtSetArg(args[0], XtNbackground, &oldbg);
1907 XtSetArg(args[1], XtNforeground, &oldfg);
1908 XtGetValues(optList[W_PAUSE].handle,
1910 XtSetArg(args[0], XtNbackground, oldfg);
1911 XtSetArg(args[1], XtNforeground, oldbg);
1913 XtSetValues(optList[W_PAUSE].handle, args, 2);
1917 wname = ModeToWidgetName(oldmode);
1918 if (wname != NULL) {
1919 MarkMenuItem(wname, False);
1921 wname = ModeToWidgetName(gameMode);
1922 if (wname != NULL) {
1923 MarkMenuItem(wname, True);
1926 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1928 /* Maybe all the enables should be handled here, not just this one */
1929 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1931 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1936 * Button/menu procedures
1939 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1940 char *selected_fen_position=NULL;
1943 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1944 Atom *type_return, XtPointer *value_return,
1945 unsigned long *length_return, int *format_return)
1947 char *selection_tmp;
1949 // if (!selected_fen_position) return False; /* should never happen */
1950 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1951 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1952 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1955 if (f == NULL) return False;
1959 selection_tmp = XtMalloc(len + 1);
1960 count = fread(selection_tmp, 1, len, f);
1963 XtFree(selection_tmp);
1966 selection_tmp[len] = NULLCHAR;
1968 /* note: since no XtSelectionDoneProc was registered, Xt will
1969 * automatically call XtFree on the value returned. So have to
1970 * make a copy of it allocated with XtMalloc */
1971 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1972 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1975 *value_return=selection_tmp;
1976 *length_return=strlen(selection_tmp);
1977 *type_return=*target;
1978 *format_return = 8; /* bits per byte */
1980 } else if (*target == XA_TARGETS(xDisplay)) {
1981 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1982 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1983 targets_tmp[1] = XA_STRING;
1984 *value_return = targets_tmp;
1985 *type_return = XA_ATOM;
1988 // This code leads to a read of value_return out of bounds on 64-bit systems.
1989 // Other code which I have seen always sets *format_return to 32 independent of
1990 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1991 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1992 *format_return = 8 * sizeof(Atom);
1993 if (*format_return > 32) {
1994 *length_return *= *format_return / 32;
1995 *format_return = 32;
1998 *format_return = 32;
2006 /* note: when called from menu all parameters are NULL, so no clue what the
2007 * Widget which was clicked on was, or what the click event was
2010 CopySomething (char *src)
2012 selected_fen_position = src;
2014 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2015 * have a notion of a position that is selected but not copied.
2016 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2018 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2020 SendPositionSelection,
2021 NULL/* lose_ownership_proc */ ,
2022 NULL/* transfer_done_proc */);
2023 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2025 SendPositionSelection,
2026 NULL/* lose_ownership_proc */ ,
2027 NULL/* transfer_done_proc */);
2030 /* function called when the data to Paste is ready */
2032 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2033 Atom *type, XtPointer value, unsigned long *len, int *format)
2036 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2037 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2038 EditPositionPasteFEN(fenstr);
2042 /* called when Paste Position button is pressed,
2043 * all parameters will be NULL */
2045 PastePositionProc ()
2047 XtGetSelectionValue(menuBarWidget,
2048 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2049 /* (XtSelectionCallbackProc) */ PastePositionCB,
2050 NULL, /* client_data passed to PastePositionCB */
2052 /* better to use the time field from the event that triggered the
2053 * call to this function, but that isn't trivial to get
2060 /* note: when called from menu all parameters are NULL, so no clue what the
2061 * Widget which was clicked on was, or what the click event was
2063 /* function called when the data to Paste is ready */
2065 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2066 Atom *type, XtPointer value, unsigned long *len, int *format)
2069 if (value == NULL || *len == 0) {
2070 return; /* nothing had been selected to copy */
2072 f = fopen(gamePasteFilename, "w");
2074 DisplayError(_("Can't open temp file"), errno);
2077 fwrite(value, 1, *len, f);
2080 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2083 /* called when Paste Game button is pressed,
2084 * all parameters will be NULL */
2088 XtGetSelectionValue(menuBarWidget,
2089 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2090 /* (XtSelectionCallbackProc) */ PasteGameCB,
2091 NULL, /* client_data passed to PasteGameCB */
2093 /* better to use the time field from the event that triggered the
2094 * call to this function, but that isn't trivial to get
2103 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2110 { // bassic primitive for determining if modifier keys are pressed
2111 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2114 XQueryKeymap(xDisplay,keys);
2115 for(i=0; i<6; i++) {
2117 j = XKeysymToKeycode(xDisplay, codes[i]);
2118 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2124 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2128 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2129 if ( n == 1 && *buf >= 32 // printable
2130 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2131 ) BoxAutoPopUp (buf);
2135 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2136 { // [HGM] input: let up-arrow recall previous line from history
2141 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2142 { // [HGM] input: let down-arrow recall next line from history
2147 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2153 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2155 if (!TempBackwardActive) {
2156 TempBackwardActive = True;
2162 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2164 /* Check to see if triggered by a key release event for a repeating key.
2165 * If so the next queued event will be a key press of the same key at the same time */
2166 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2168 XPeekEvent(xDisplay, &next);
2169 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2170 next.xkey.keycode == event->xkey.keycode)
2174 TempBackwardActive = False;
2178 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2179 { // called as key binding
2182 if (nprms && *nprms > 0)
2186 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2192 { // called from menu
2193 ManInner(NULL, NULL, NULL, NULL);
2197 SetWindowTitle (char *text, char *title, char *icon)
2201 if (appData.titleInWindow) {
2203 XtSetArg(args[i], XtNlabel, text); i++;
2204 XtSetValues(titleWidget, args, i);
2207 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2208 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2209 XtSetValues(shellWidget, args, i);
2210 XSync(xDisplay, False);
2215 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2221 DisplayIcsInteractionTitle (String message)
2223 if (oldICSInteractionTitle == NULL) {
2224 /* Magic to find the old window title, adapted from vim */
2225 char *wina = getenv("WINDOWID");
2227 Window win = (Window) atoi(wina);
2228 Window root, parent, *children;
2229 unsigned int nchildren;
2230 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2232 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2233 if (!XQueryTree(xDisplay, win, &root, &parent,
2234 &children, &nchildren)) break;
2235 if (children) XFree((void *)children);
2236 if (parent == root || parent == 0) break;
2239 XSetErrorHandler(oldHandler);
2241 if (oldICSInteractionTitle == NULL) {
2242 oldICSInteractionTitle = "xterm";
2245 printf("\033]0;%s\007", message);
2250 XtIntervalId delayedEventTimerXID = 0;
2251 DelayedEventCallback delayedEventCallback = 0;
2256 delayedEventTimerXID = 0;
2257 delayedEventCallback();
2261 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2263 if(delayedEventTimerXID && delayedEventCallback == cb)
2264 // [HGM] alive: replace, rather than add or flush identical event
2265 XtRemoveTimeOut(delayedEventTimerXID);
2266 delayedEventCallback = cb;
2267 delayedEventTimerXID =
2268 XtAppAddTimeOut(appContext, millisec,
2269 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2272 DelayedEventCallback
2275 if (delayedEventTimerXID) {
2276 return delayedEventCallback;
2283 CancelDelayedEvent ()
2285 if (delayedEventTimerXID) {
2286 XtRemoveTimeOut(delayedEventTimerXID);
2287 delayedEventTimerXID = 0;
2291 XtIntervalId loadGameTimerXID = 0;
2294 LoadGameTimerRunning ()
2296 return loadGameTimerXID != 0;
2300 StopLoadGameTimer ()
2302 if (loadGameTimerXID != 0) {
2303 XtRemoveTimeOut(loadGameTimerXID);
2304 loadGameTimerXID = 0;
2312 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2314 loadGameTimerXID = 0;
2319 StartLoadGameTimer (long millisec)
2322 XtAppAddTimeOut(appContext, millisec,
2323 (XtTimerCallbackProc) LoadGameTimerCallback,
2327 XtIntervalId analysisClockXID = 0;
2330 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2332 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2333 || appData.icsEngineAnalyze) { // [DM]
2334 AnalysisPeriodicEvent(0);
2335 StartAnalysisClock();
2340 StartAnalysisClock ()
2343 XtAppAddTimeOut(appContext, 2000,
2344 (XtTimerCallbackProc) AnalysisClockCallback,
2348 XtIntervalId clockTimerXID = 0;
2351 ClockTimerRunning ()
2353 return clockTimerXID != 0;
2359 if (clockTimerXID != 0) {
2360 XtRemoveTimeOut(clockTimerXID);
2369 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2376 StartClockTimer (long millisec)
2379 XtAppAddTimeOut(appContext, millisec,
2380 (XtTimerCallbackProc) ClockTimerCallback,
2385 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2389 Widget w = (Widget) opt->handle;
2391 /* check for low time warning */
2392 Pixel foregroundOrWarningColor = timerForegroundPixel;
2395 appData.lowTimeWarning &&
2396 (timer / 1000) < appData.icsAlarmTime)
2397 foregroundOrWarningColor = lowTimeWarningColor;
2399 if (appData.clockMode) {
2400 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2401 XtSetArg(args[0], XtNlabel, buf);
2403 snprintf(buf, MSG_SIZ, "%s ", color);
2404 XtSetArg(args[0], XtNlabel, buf);
2409 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2410 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2412 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2413 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2416 XtSetValues(w, args, 3);
2419 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2422 SetClockIcon (int color)
2425 Pixmap pm = *clockIcons[color];
2426 if (iconPixmap != pm) {
2428 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2429 XtSetValues(shellWidget, args, 1);
2433 #define INPUT_SOURCE_BUF_SIZE 8192
2442 char buf[INPUT_SOURCE_BUF_SIZE];
2447 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2449 InputSource *is = (InputSource *) closure;
2454 if (is->lineByLine) {
2455 count = read(is->fd, is->unused,
2456 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2458 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2461 is->unused += count;
2463 while (p < is->unused) {
2464 q = memchr(p, '\n', is->unused - p);
2465 if (q == NULL) break;
2467 (is->func)(is, is->closure, p, q - p, 0);
2471 while (p < is->unused) {
2476 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2481 (is->func)(is, is->closure, is->buf, count, error);
2486 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2489 ChildProc *cp = (ChildProc *) pr;
2491 is = (InputSource *) calloc(1, sizeof(InputSource));
2492 is->lineByLine = lineByLine;
2496 is->fd = fileno(stdin);
2498 is->kind = cp->kind;
2499 is->fd = cp->fdFrom;
2502 is->unused = is->buf;
2505 is->xid = XtAppAddInput(appContext, is->fd,
2506 (XtPointer) (XtInputReadMask),
2507 (XtInputCallbackProc) DoInputCallback,
2509 is->closure = closure;
2510 return (InputSourceRef) is;
2514 RemoveInputSource (InputSourceRef isr)
2516 InputSource *is = (InputSource *) isr;
2518 if (is->xid == 0) return;
2519 XtRemoveInput(is->xid);
2525 static Boolean frameWaiting;
2528 FrameAlarm (int sig)
2530 frameWaiting = False;
2531 /* In case System-V style signals. Needed?? */
2532 signal(SIGALRM, FrameAlarm);
2536 FrameDelay (int time)
2538 struct itimerval delay;
2540 XSync(xDisplay, False);
2543 frameWaiting = True;
2544 signal(SIGALRM, FrameAlarm);
2545 delay.it_interval.tv_sec =
2546 delay.it_value.tv_sec = time / 1000;
2547 delay.it_interval.tv_usec =
2548 delay.it_value.tv_usec = (time % 1000) * 1000;
2549 setitimer(ITIMER_REAL, &delay, NULL);
2550 while (frameWaiting) pause();
2551 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2552 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2553 setitimer(ITIMER_REAL, &delay, NULL);
2560 FrameDelay (int time)
2562 XSync(xDisplay, False);
2564 usleep(time * 1000);
2570 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2572 char buf[MSG_SIZ], *logoName = buf;
2573 if(appData.logo[n][0]) {
2574 logoName = appData.logo[n];
2575 } else if(appData.autoLogo) {
2576 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2577 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2578 } else if(appData.directory[n] && appData.directory[n][0]) {
2579 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2583 { ASSIGN(cps->programLogo, logoName); }
2587 UpdateLogos (int displ)
2589 if(optList[W_WHITE-1].handle == NULL) return;
2590 LoadLogo(&first, 0, 0);
2591 LoadLogo(&second, 1, appData.icsActive);
2592 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);