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;
322 /* This magic number is the number of intermediate frames used
323 in each half of the animation. For short moves it's reduced
324 by 1. The total number of frames will be factor * 2 + 1. */
327 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
334 DropMenuEnables dmEnables[] = {
351 XtResource clientResources[] = {
352 { "flashCount", "flashCount", XtRInt, sizeof(int),
353 XtOffset(AppDataPtr, flashCount), XtRImmediate,
354 (XtPointer) FLASH_COUNT },
357 XrmOptionDescRec shellOptions[] = {
358 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
359 { "-flash", "flashCount", XrmoptionNoArg, "3" },
360 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
363 XtActionsRec boardActions[] = {
364 { "DrawPosition", DrawPositionProc },
365 { "HandlePV", HandlePV },
366 { "SelectPV", SelectPV },
367 { "StopPV", StopPV },
368 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
369 { "QuitProc", QuitWrapper },
370 { "ManProc", ManInner },
371 { "TempBackwardProc", TempBackwardProc },
372 { "TempForwardProc", TempForwardProc },
373 { "CommentClick", (XtActionProc) CommentClick },
374 { "GenericPopDown", (XtActionProc) GenericPopDown },
375 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
376 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
377 { "SelectMove", (XtActionProc) SelectMoveX },
378 { "LoadSelectedProc", LoadSelectedProc },
379 { "SetFilterProc", SetFilterProc },
380 { "TypeInProc", TypeInProc },
381 { "EnterKeyProc", EnterKeyProc },
382 { "UpKeyProc", UpKeyProc },
383 { "DownKeyProc", DownKeyProc },
384 { "WheelProc", WheelProc },
385 { "TabProc", TabProc },
388 char globalTranslations[] =
389 ":Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
390 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
391 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
392 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
393 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
394 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
395 :<Key>Pause: MenuItem(Mode.Pause) \n \
396 :Ctrl<Key>d: MenuItem(DebugProc) \n \
397 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
398 :<Key>Left: MenuItem(Edit.Backward) \n \
399 :<Key>Right: MenuItem(Edit.Forward) \n \
400 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
401 #ifndef OPTIONSDIALOG
403 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
404 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
405 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
406 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
407 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
410 :<KeyDown>Return: TempBackwardProc() \n \
411 :<KeyUp>Return: TempForwardProc() \n";
413 char ICSInputTranslations[] =
414 "<Key>Up: UpKeyProc() \n "
415 "<Key>Down: DownKeyProc() \n "
416 "<Key>Return: EnterKeyProc() \n";
418 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
419 // as the widget is destroyed before the up-click can call extend-end
420 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
422 String xboardResources[] = {
423 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
428 /* Max possible square size */
429 #define MAXSQSIZE 256
431 static int xpm_avail[MAXSQSIZE];
433 #ifdef HAVE_DIR_STRUCT
435 /* Extract piece size from filename */
437 xpm_getsize (char *name, int len, char *ext)
445 if ((p=strchr(name, '.')) == NULL ||
446 StrCaseCmp(p+1, ext) != 0)
452 while (*p && isdigit(*p))
459 /* Setup xpm_avail */
461 xpm_getavail (char *dirname, char *ext)
467 for (i=0; i<MAXSQSIZE; ++i)
470 if (appData.debugMode)
471 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
473 dir = opendir(dirname);
476 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
477 programName, dirname);
481 while ((ent=readdir(dir)) != NULL) {
482 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
483 if (i > 0 && i < MAXSQSIZE)
493 xpm_print_avail (FILE *fp, char *ext)
497 fprintf(fp, _("Available `%s' sizes:\n"), ext);
498 for (i=1; i<MAXSQSIZE; ++i) {
504 /* Return XPM piecesize closest to size */
506 xpm_closest_to (char *dirname, int size, char *ext)
509 int sm_diff = MAXSQSIZE;
513 xpm_getavail(dirname, ext);
515 if (appData.debugMode)
516 xpm_print_avail(stderr, ext);
518 for (i=1; i<MAXSQSIZE; ++i) {
521 diff = (diff<0) ? -diff : diff;
522 if (diff < sm_diff) {
530 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
536 #else /* !HAVE_DIR_STRUCT */
537 /* If we are on a system without a DIR struct, we can't
538 read the directory, so we can't collect a list of
539 filenames, etc., so we can't do any size-fitting. */
541 xpm_closest_to (char *dirname, int size, char *ext)
544 Warning: No DIR structure found on this system --\n\
545 Unable to autosize for XPM/XIM pieces.\n\
546 Please report this error to %s.\n\
547 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
550 #endif /* HAVE_DIR_STRUCT */
553 /* Arrange to catch delete-window events */
554 Atom wm_delete_window;
556 CatchDeleteWindow (Widget w, String procname)
559 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
560 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
561 XtAugmentTranslations(w, XtParseTranslationTable(buf));
568 XtSetArg(args[0], XtNiconic, False);
569 XtSetValues(shellWidget, args, 1);
571 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
574 //---------------------------------------------------------------------------------------------------------
575 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
578 #define CW_USEDEFAULT (1<<31)
579 #define ICS_TEXT_MENU_SIZE 90
580 #define DEBUG_FILE "xboard.debug"
581 #define SetCurrentDirectory chdir
582 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
586 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
589 // front-end part of option handling
591 // [HGM] This platform-dependent table provides the location for storing the color info
592 extern char *crWhite, * crBlack;
596 &appData.whitePieceColor,
597 &appData.blackPieceColor,
598 &appData.lightSquareColor,
599 &appData.darkSquareColor,
600 &appData.highlightSquareColor,
601 &appData.premoveHighlightColor,
602 &appData.lowTimeWarningColor,
613 // [HGM] font: keep a font for each square size, even non-stndard ones
616 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
617 char *fontTable[NUM_FONTS][MAX_SIZE];
620 ParseFont (char *name, int number)
621 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
623 if(sscanf(name, "size%d:", &size)) {
624 // [HGM] font: font is meant for specific boardSize (likely from settings file);
625 // defer processing it until we know if it matches our board size
626 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
627 fontTable[number][size] = strdup(strchr(name, ':')+1);
628 fontValid[number][size] = True;
633 case 0: // CLOCK_FONT
634 appData.clockFont = strdup(name);
636 case 1: // MESSAGE_FONT
637 appData.font = strdup(name);
639 case 2: // COORD_FONT
640 appData.coordFont = strdup(name);
645 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
650 { // only 2 fonts currently
651 appData.clockFont = CLOCK_FONT_NAME;
652 appData.coordFont = COORD_FONT_NAME;
653 appData.font = DEFAULT_FONT_NAME;
658 { // no-op, until we identify the code for this already in XBoard and move it here
662 ParseColor (int n, char *name)
663 { // in XBoard, just copy the color-name string
664 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
668 ParseTextAttribs (ColorClass cc, char *s)
670 (&appData.colorShout)[cc] = strdup(s);
674 ParseBoardSize (void *addr, char *name)
676 appData.boardSize = strdup(name);
681 { // In XBoard the sound-playing program takes care of obtaining the actual sound
685 SetCommPortDefaults ()
686 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
689 // [HGM] args: these three cases taken out to stay in front-end
691 SaveFontArg (FILE *f, ArgDescriptor *ad)
694 int i, n = (int)(intptr_t)ad->argLoc;
696 case 0: // CLOCK_FONT
697 name = appData.clockFont;
699 case 1: // MESSAGE_FONT
702 case 2: // COORD_FONT
703 name = appData.coordFont;
708 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
709 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
710 fontTable[n][squareSize] = strdup(name);
711 fontValid[n][squareSize] = True;
714 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
715 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
720 { // nothing to do, as the sounds are at all times represented by their text-string names already
724 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
725 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
726 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
730 SaveColor (FILE *f, ArgDescriptor *ad)
731 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
732 if(colorVariable[(int)(intptr_t)ad->argLoc])
733 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
737 SaveBoardSize (FILE *f, char *name, void *addr)
738 { // wrapper to shield back-end from BoardSize & sizeInfo
739 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
743 ParseCommPortSettings (char *s)
744 { // no such option in XBoard (yet)
750 GetActualPlacement (Widget wg, WindowPlacement *wp)
752 XWindowAttributes winAt;
759 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
760 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
761 wp->x = rx - winAt.x;
762 wp->y = ry - winAt.y;
763 wp->height = winAt.height;
764 wp->width = winAt.width;
765 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
770 { // wrapper to shield use of window handles from back-end (make addressible by number?)
771 // In XBoard this will have to wait until awareness of window parameters is implemented
772 GetActualPlacement(shellWidget, &wpMain);
773 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
774 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
775 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
776 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
777 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
778 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
782 PrintCommPortSettings (FILE *f, char *name)
783 { // This option does not exist in XBoard
787 EnsureOnScreen (int *x, int *y, int minX, int minY)
794 { // [HGM] args: allows testing if main window is realized from back-end
795 return xBoardWindow != 0;
799 PopUpStartupDialog ()
800 { // start menu not implemented in XBoard
804 ConvertToLine (int argc, char **argv)
806 static char line[128*1024], buf[1024];
810 for(i=1; i<argc; i++)
812 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
813 && argv[i][0] != '{' )
814 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
816 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
817 strncat(line, buf, 128*1024 - strlen(line) - 1 );
820 line[strlen(line)-1] = NULLCHAR;
824 //--------------------------------------------------------------------------------------------
827 ResizeBoardWindow (int w, int h, int inhibit)
829 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
831 shellArgs[0].value = w;
832 shellArgs[1].value = h;
833 shellArgs[4].value = shellArgs[2].value = w;
834 shellArgs[5].value = shellArgs[3].value = h;
835 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
837 XSync(xDisplay, False);
841 MakeOneColor (char *name, Pixel *color)
844 if (!appData.monoMode) {
845 vFrom.addr = (caddr_t) name;
846 vFrom.size = strlen(name);
847 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
848 if (vTo.addr == NULL) {
849 appData.monoMode = True;
852 *color = *(Pixel *) vTo.addr;
860 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
861 int forceMono = False;
863 if (appData.lowTimeWarning)
864 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
865 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
866 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
872 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
873 { // detervtomine what fonts to use, and create them
877 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
878 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
879 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
880 appData.font = fontTable[MESSAGE_FONT][squareSize];
881 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
882 appData.coordFont = fontTable[COORD_FONT][squareSize];
885 appData.font = InsertPxlSize(appData.font, fontPxlSize);
886 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
887 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
888 fontSet = CreateFontSet(appData.font);
889 clockFontSet = CreateFontSet(appData.clockFont);
891 /* For the coordFont, use the 0th font of the fontset. */
892 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
893 XFontStruct **font_struct_list;
894 XFontSetExtents *fontSize;
895 char **font_name_list;
896 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
897 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
898 coordFontStruct = XQueryFont(xDisplay, coordFontID);
899 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
900 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
903 appData.font = FindFont(appData.font, fontPxlSize);
904 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
905 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
906 clockFontID = XLoadFont(xDisplay, appData.clockFont);
907 clockFontStruct = XQueryFont(xDisplay, clockFontID);
908 coordFontID = XLoadFont(xDisplay, appData.coordFont);
909 coordFontStruct = XQueryFont(xDisplay, coordFontID);
910 // textHeight in !NLS mode!
912 countFontID = coordFontID; // [HGM] holdings
913 countFontStruct = coordFontStruct;
915 xdb = XtDatabase(xDisplay);
917 XrmPutLineResource(&xdb, "*international: True");
918 vTo.size = sizeof(XFontSet);
919 vTo.addr = (XtPointer) &fontSet;
920 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
922 XrmPutStringResource(&xdb, "*font", appData.font);
932 case ArgInt: p = " N"; break;
933 case ArgString: p = " STR"; break;
934 case ArgBoolean: p = " TF"; break;
935 case ArgSettingsFilename:
936 case ArgFilename: p = " FILE"; break;
937 case ArgX: p = " Nx"; break;
938 case ArgY: p = " Ny"; break;
939 case ArgAttribs: p = " TEXTCOL"; break;
940 case ArgColor: p = " COL"; break;
941 case ArgFont: p = " FONT"; break;
942 case ArgBoardSize: p = " SIZE"; break;
943 case ArgFloat: p = " FLOAT"; break;
948 case ArgCommSettings:
955 GenerateGlobalTranslationTable (void)
957 /* go through all menu items and extract the keyboard shortcuts, so that X11 can load them */
965 /* loop over all menu entries */
966 for( i=0; menuBar[i].mi ; i++)
969 for(j=0; mi[j].proc; j++)
977 char *key,*test, *mods;
979 /* check for Ctrl/Alt */
980 if( strstr(mi[j].accel, "<Ctrl>") ) ctrl = 1;
981 if( strstr(mi[j].accel, "<Shift>") ) shift = 1;
982 if( strstr(mi[j].accel, "<Alt>") ) alt = 1;
984 /* remove all <...> */
985 test = strrchr(mi[j].accel, '>');
987 key = strdup(mi[j].accel);
989 key = strdup(++test); // remove ">"
991 /* instead of shift X11 uses the uppercase letter directly*/
992 if (shift && strlen(key)==1 )
994 *key = toupper(*key);
998 /* handle some special cases which have different names in X11 */
999 if ( strncmp(key, "Page_Down", 9) == 0 )
1004 else if ( strncmp(key, "Page_Up", 7) == 0 )
1007 key=strdup("Prior");
1010 /* create string of mods */
1012 mods = strdup("Ctrl ");
1018 mods = realloc(mods, strlen(mods) + strlen("Meta ")+1);
1019 strncat(mods, "Meta ", 5);
1024 mods = realloc(mods, strlen(mods) + strlen("Shift ")+1);
1025 strncat(mods, "Shift ", 6);
1028 // remove trailing space
1029 if( isspace(mods[strlen(mods)-1]) )
1030 mods[strlen(mods)-1]='\0';
1032 /* get the name for the callback, we can use MenuItem() here that will call KeyBindingProc */
1033 size_t namesize = snprintf(NULL, 0, "%s.%s", menuBar[i].ref, mi[j].ref);
1034 char *name = malloc(namesize+1);
1035 snprintf(name, namesize+1, "%s.%s", menuBar[i].ref, mi[j].ref);
1037 size_t buffersize = snprintf(NULL, 0, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
1038 char *buffer = malloc(buffersize+1);
1039 snprintf(buffer, buffersize+1, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
1041 /* add string to the output */
1042 output = realloc(output, strlen(output) + strlen(buffer)+1);
1043 strncat(output, buffer, strlen(buffer));
1062 ArgDescriptor *q, *p = argDescriptors+5;
1063 printf("\nXBoard accepts the following options:\n"
1064 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1065 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1066 " SIZE = board-size spec(s)\n"
1067 " Within parentheses are short forms, or options to set to true or false.\n"
1068 " Persistent options (saved in the settings file) are marked with *)\n\n");
1070 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1071 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1072 if(p->save) strcat(buf+len, "*");
1073 for(q=p+1; q->argLoc == p->argLoc; q++) {
1074 if(q->argName[0] == '-') continue;
1075 strcat(buf+len, q == p+1 ? " (" : " ");
1076 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1078 if(q != p+1) strcat(buf+len, ")");
1080 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1083 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1087 main (int argc, char **argv)
1089 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1090 XSetWindowAttributes window_attributes;
1092 Dimension boardWidth, boardHeight, w, h;
1094 int forceMono = False;
1096 srandom(time(0)); // [HGM] book: make random truly random
1098 setbuf(stdout, NULL);
1099 setbuf(stderr, NULL);
1102 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1103 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1107 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1112 programName = strrchr(argv[0], '/');
1113 if (programName == NULL)
1114 programName = argv[0];
1119 XtSetLanguageProc(NULL, NULL, NULL);
1120 if (appData.debugMode) {
1121 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1124 bindtextdomain(PACKAGE, LOCALEDIR);
1125 textdomain(PACKAGE);
1128 appData.boardSize = "";
1129 InitAppData(ConvertToLine(argc, argv));
1131 if (p == NULL) p = "/tmp";
1132 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1133 gameCopyFilename = (char*) malloc(i);
1134 gamePasteFilename = (char*) malloc(i);
1135 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1136 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1138 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1139 static char buf[MSG_SIZ];
1140 EscapeExpand(buf, appData.firstInitString);
1141 appData.firstInitString = strdup(buf);
1142 EscapeExpand(buf, appData.secondInitString);
1143 appData.secondInitString = strdup(buf);
1144 EscapeExpand(buf, appData.firstComputerString);
1145 appData.firstComputerString = strdup(buf);
1146 EscapeExpand(buf, appData.secondComputerString);
1147 appData.secondComputerString = strdup(buf);
1150 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1153 if (chdir(chessDir) != 0) {
1154 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1160 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1161 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1162 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1163 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1166 setbuf(debugFP, NULL);
1169 /* [HGM,HR] make sure board size is acceptable */
1170 if(appData.NrFiles > BOARD_FILES ||
1171 appData.NrRanks > BOARD_RANKS )
1172 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1175 /* This feature does not work; animation needs a rewrite */
1176 appData.highlightDragging = FALSE;
1180 gameInfo.variant = StringToVariant(appData.variant);
1181 InitPosition(FALSE);
1184 XtAppInitialize(&appContext, "XBoard", shellOptions,
1185 XtNumber(shellOptions),
1186 &argc, argv, xboardResources, NULL, 0);
1188 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1189 clientResources, XtNumber(clientResources),
1192 xDisplay = XtDisplay(shellWidget);
1193 xScreen = DefaultScreen(xDisplay);
1194 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1197 * determine size, based on supplied or remembered -size, or screen size
1199 if (isdigit(appData.boardSize[0])) {
1200 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1201 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1202 &fontPxlSize, &smallLayout, &tinyLayout);
1204 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1205 programName, appData.boardSize);
1209 /* Find some defaults; use the nearest known size */
1210 SizeDefaults *szd, *nearest;
1211 int distance = 99999;
1212 nearest = szd = sizeDefaults;
1213 while (szd->name != NULL) {
1214 if (abs(szd->squareSize - squareSize) < distance) {
1216 distance = abs(szd->squareSize - squareSize);
1217 if (distance == 0) break;
1221 if (i < 2) lineGap = nearest->lineGap;
1222 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1223 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1224 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1225 if (i < 6) smallLayout = nearest->smallLayout;
1226 if (i < 7) tinyLayout = nearest->tinyLayout;
1229 SizeDefaults *szd = sizeDefaults;
1230 if (*appData.boardSize == NULLCHAR) {
1231 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1232 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1235 if (szd->name == NULL) szd--;
1236 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1238 while (szd->name != NULL &&
1239 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1240 if (szd->name == NULL) {
1241 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1242 programName, appData.boardSize);
1246 squareSize = szd->squareSize;
1247 lineGap = szd->lineGap;
1248 clockFontPxlSize = szd->clockFontPxlSize;
1249 coordFontPxlSize = szd->coordFontPxlSize;
1250 fontPxlSize = szd->fontPxlSize;
1251 smallLayout = szd->smallLayout;
1252 tinyLayout = szd->tinyLayout;
1253 // [HGM] font: use defaults from settings file if available and not overruled
1256 defaultLineGap = lineGap;
1257 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1259 /* [HR] height treated separately (hacked) */
1260 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1261 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1264 * Determine what fonts to use.
1266 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1269 * Detect if there are not enough colors available and adapt.
1271 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1272 appData.monoMode = True;
1275 forceMono = MakeColors();
1278 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1280 appData.monoMode = True;
1283 if (appData.monoMode && appData.debugMode) {
1284 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1285 (unsigned long) XWhitePixel(xDisplay, xScreen),
1286 (unsigned long) XBlackPixel(xDisplay, xScreen));
1289 ParseIcsTextColors();
1291 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1297 layoutName = "tinyLayout";
1298 } else if (smallLayout) {
1299 layoutName = "smallLayout";
1301 layoutName = "normalLayout";
1304 optList = BoardPopUp(squareSize, lineGap, (void*)
1310 InitDrawingHandle(optList + W_BOARD);
1311 currBoard = &optList[W_BOARD];
1312 boardWidget = optList[W_BOARD].handle;
1313 menuBarWidget = optList[W_MENU].handle;
1314 dropMenu = optList[W_DROP].handle;
1315 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1316 formWidget = XtParent(boardWidget);
1317 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1318 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1319 XtGetValues(optList[W_WHITE].handle, args, 2);
1320 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1321 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1322 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1323 XtGetValues(optList[W_PAUSE].handle, args, 2);
1326 xBoardWindow = XtWindow(boardWidget);
1328 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1329 // not need to go into InitDrawingSizes().
1332 * Create X checkmark bitmap and initialize option menu checks.
1334 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1335 checkmark_bits, checkmark_width, checkmark_height);
1341 ReadBitmap(&wIconPixmap, "icon_white.bm",
1342 icon_white_bits, icon_white_width, icon_white_height);
1343 ReadBitmap(&bIconPixmap, "icon_black.bm",
1344 icon_black_bits, icon_black_width, icon_black_height);
1345 iconPixmap = wIconPixmap;
1347 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1348 XtSetValues(shellWidget, args, i);
1351 * Create a cursor for the board widget.
1353 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1354 XChangeWindowAttributes(xDisplay, xBoardWindow,
1355 CWCursor, &window_attributes);
1358 * Inhibit shell resizing.
1360 shellArgs[0].value = (XtArgVal) &w;
1361 shellArgs[1].value = (XtArgVal) &h;
1362 XtGetValues(shellWidget, shellArgs, 2);
1363 shellArgs[4].value = shellArgs[2].value = w;
1364 shellArgs[5].value = shellArgs[3].value = h;
1365 // XtSetValues(shellWidget, &shellArgs[2], 4);
1366 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1367 marginH = h - boardHeight;
1369 CatchDeleteWindow(shellWidget, "QuitProc");
1374 if(appData.logoSize)
1375 { // locate and read user logo
1377 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1378 ASSIGN(userLogo, buf);
1381 if (appData.animate || appData.animateDragging)
1385 char *TranslationsTableMenus=GenerateGlobalTranslationTable ();
1387 XtAugmentTranslations(formWidget,
1388 XtParseTranslationTable(globalTranslations));
1389 XtAugmentTranslations(formWidget,
1390 XtParseTranslationTable(TranslationsTableMenus));
1392 XtAddEventHandler(formWidget, KeyPressMask, False,
1393 (XtEventHandler) MoveTypeInProc, NULL);
1394 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1395 (XtEventHandler) EventProc, NULL);
1397 /* [AS] Restore layout */
1398 if( wpMoveHistory.visible ) {
1402 if( wpEvalGraph.visible )
1407 if( wpEngineOutput.visible ) {
1408 EngineOutputPopUp();
1413 if (errorExitStatus == -1) {
1414 if (appData.icsActive) {
1415 /* We now wait until we see "login:" from the ICS before
1416 sending the logon script (problems with timestamp otherwise) */
1417 /*ICSInitScript();*/
1418 if (appData.icsInputBox) ICSInputBoxPopUp();
1422 signal(SIGWINCH, TermSizeSigHandler);
1424 signal(SIGINT, IntSigHandler);
1425 signal(SIGTERM, IntSigHandler);
1426 if (*appData.cmailGameName != NULLCHAR) {
1427 signal(SIGUSR1, CmailSigHandler);
1431 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1434 // XtSetKeyboardFocus(shellWidget, formWidget);
1435 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1437 XtAppMainLoop(appContext);
1438 if (appData.debugMode) fclose(debugFP); // [DM] debug
1443 TermSizeSigHandler (int sig)
1449 IntSigHandler (int sig)
1455 CmailSigHandler (int sig)
1460 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1462 /* Activate call-back function CmailSigHandlerCallBack() */
1463 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1465 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1469 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1472 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1474 /**** end signal code ****/
1477 #define Abs(n) ((n)<0 ? -(n) : (n))
1481 InsertPxlSize (char *pattern, int targetPxlSize)
1483 char *base_fnt_lst, strInt[12], *p, *q;
1484 int alternatives, i, len, strIntLen;
1487 * Replace the "*" (if present) in the pixel-size slot of each
1488 * alternative with the targetPxlSize.
1492 while ((p = strchr(p, ',')) != NULL) {
1496 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1497 strIntLen = strlen(strInt);
1498 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1502 while (alternatives--) {
1503 char *comma = strchr(p, ',');
1504 for (i=0; i<14; i++) {
1505 char *hyphen = strchr(p, '-');
1507 if (comma && hyphen > comma) break;
1508 len = hyphen + 1 - p;
1509 if (i == 7 && *p == '*' && len == 2) {
1511 memcpy(q, strInt, strIntLen);
1521 len = comma + 1 - p;
1528 return base_fnt_lst;
1532 CreateFontSet (char *base_fnt_lst)
1535 char **missing_list;
1539 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1540 &missing_list, &missing_count, &def_string);
1541 if (appData.debugMode) {
1543 XFontStruct **font_struct_list;
1544 char **font_name_list;
1545 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1547 fprintf(debugFP, " got list %s, locale %s\n",
1548 XBaseFontNameListOfFontSet(fntSet),
1549 XLocaleOfFontSet(fntSet));
1550 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1551 for (i = 0; i < count; i++) {
1552 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1555 for (i = 0; i < missing_count; i++) {
1556 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1559 if (fntSet == NULL) {
1560 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1565 #else // not ENABLE_NLS
1567 * Find a font that matches "pattern" that is as close as
1568 * possible to the targetPxlSize. Prefer fonts that are k
1569 * pixels smaller to fonts that are k pixels larger. The
1570 * pattern must be in the X Consortium standard format,
1571 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1572 * The return value should be freed with XtFree when no
1576 FindFont (char *pattern, int targetPxlSize)
1578 char **fonts, *p, *best, *scalable, *scalableTail;
1579 int i, j, nfonts, minerr, err, pxlSize;
1581 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1583 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1584 programName, pattern);
1591 for (i=0; i<nfonts; i++) {
1594 if (*p != '-') continue;
1596 if (*p == NULLCHAR) break;
1597 if (*p++ == '-') j++;
1599 if (j < 7) continue;
1602 scalable = fonts[i];
1605 err = pxlSize - targetPxlSize;
1606 if (Abs(err) < Abs(minerr) ||
1607 (minerr > 0 && err < 0 && -err == minerr)) {
1613 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1614 /* If the error is too big and there is a scalable font,
1615 use the scalable font. */
1616 int headlen = scalableTail - scalable;
1617 p = (char *) XtMalloc(strlen(scalable) + 10);
1618 while (isdigit(*scalableTail)) scalableTail++;
1619 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1621 p = (char *) XtMalloc(strlen(best) + 2);
1622 safeStrCpy(p, best, strlen(best)+1 );
1624 if (appData.debugMode) {
1625 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1626 pattern, targetPxlSize, p);
1628 XFreeFontNames(fonts);
1634 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1637 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1643 MarkMenuItem (char *menuRef, int state)
1645 MenuItem *item = MenuNameToItem(menuRef);
1649 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1650 XtSetValues(item->handle, args, 1);
1655 EnableNamedMenuItem (char *menuRef, int state)
1657 MenuItem *item = MenuNameToItem(menuRef);
1659 if(item) XtSetSensitive(item->handle, state);
1663 EnableButtonBar (int state)
1665 XtSetSensitive(optList[W_BUTTON].handle, state);
1670 SetMenuEnables (Enables *enab)
1672 while (enab->name != NULL) {
1673 EnableNamedMenuItem(enab->name, enab->value);
1679 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1680 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1682 if(*nprms == 0) return;
1683 item = MenuNameToItem(prms[0]);
1684 if(item) ((MenuProc *) item->proc) ();
1696 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1697 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1698 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1699 dmEnables[i].piece);
1700 XtSetSensitive(entry, p != NULL || !appData.testLegality
1701 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1702 && !appData.icsActive));
1704 while (p && *p++ == dmEnables[i].piece) count++;
1705 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1707 XtSetArg(args[j], XtNlabel, label); j++;
1708 XtSetValues(entry, args, j);
1713 do_flash_delay (unsigned long msec)
1719 FlashDelay (int flash_delay)
1721 XSync(xDisplay, False);
1722 if(flash_delay) do_flash_delay(flash_delay);
1726 Fraction (int x, int start, int stop)
1728 double f = ((double) x - start)/(stop - start);
1729 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1733 static WindowPlacement wpNew;
1736 CoDrag (Widget sh, WindowPlacement *wp)
1739 int j=0, touch=0, fudge = 2;
1740 GetActualPlacement(sh, wp);
1741 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1742 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1743 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1744 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1745 if(!touch ) return; // only windows that touch co-move
1746 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1747 int heightInc = wpNew.height - wpMain.height;
1748 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1749 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1750 wp->y += fracTop * heightInc;
1751 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1752 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1753 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1754 int widthInc = wpNew.width - wpMain.width;
1755 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1756 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1757 wp->y += fracLeft * widthInc;
1758 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1759 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1761 wp->x += wpNew.x - wpMain.x;
1762 wp->y += wpNew.y - wpMain.y;
1763 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1764 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1765 XtSetArg(args[j], XtNx, wp->x); j++;
1766 XtSetArg(args[j], XtNy, wp->y); j++;
1767 XtSetValues(sh, args, j);
1771 ReSize (WindowPlacement *wp)
1774 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1775 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1776 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1777 if(sqy < sqx) sqx = sqy;
1778 if(sqx != squareSize) {
1779 squareSize = sqx; // adopt new square size
1780 CreatePNGPieces(); // make newly scaled pieces
1781 InitDrawingSizes(0, 0); // creates grid etc.
1782 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1783 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1784 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1785 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1786 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1789 static XtIntervalId delayedDragID = 0;
1798 GetActualPlacement(shellWidget, &wpNew);
1799 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1800 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1801 busy = 0; return; // false alarm
1804 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1805 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1806 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1807 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1809 DrawPosition(True, NULL);
1810 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1818 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1820 XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1824 EventProc (Widget widget, caddr_t unused, XEvent *event)
1826 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1827 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1831 * event handler for redrawing the board
1834 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1836 DrawPosition(True, NULL);
1841 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1842 { // [HGM] pv: walk PV
1843 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1846 extern int savedIndex; /* gross that this is global */
1849 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1852 XawTextPosition index, dummy;
1855 XawTextGetSelectionPos(w, &index, &dummy);
1856 XtSetArg(arg, XtNstring, &val);
1857 XtGetValues(w, &arg, 1);
1858 ReplaceComment(savedIndex, val);
1859 if(savedIndex != currentMove) ToNrEvent(savedIndex);
1860 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1864 /* Disable all user input other than deleting the window */
1865 static int frozen = 0;
1871 /* Grab by a widget that doesn't accept input */
1872 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1876 /* Undo a FreezeUI */
1880 if (!frozen) return;
1881 XtRemoveGrab(optList[W_MESSG].handle);
1889 static int oldPausing = FALSE;
1890 static GameMode oldmode = (GameMode) -1;
1893 if (!boardWidget || !XtIsRealized(boardWidget)) return;
1895 if (pausing != oldPausing) {
1896 oldPausing = pausing;
1897 MarkMenuItem("Mode.Pause", pausing);
1899 if (appData.showButtonBar) {
1900 /* Always toggle, don't set. Previous code messes up when
1901 invoked while the button is pressed, as releasing it
1902 toggles the state again. */
1905 XtSetArg(args[0], XtNbackground, &oldbg);
1906 XtSetArg(args[1], XtNforeground, &oldfg);
1907 XtGetValues(optList[W_PAUSE].handle,
1909 XtSetArg(args[0], XtNbackground, oldfg);
1910 XtSetArg(args[1], XtNforeground, oldbg);
1912 XtSetValues(optList[W_PAUSE].handle, args, 2);
1916 wname = ModeToWidgetName(oldmode);
1917 if (wname != NULL) {
1918 MarkMenuItem(wname, False);
1920 wname = ModeToWidgetName(gameMode);
1921 if (wname != NULL) {
1922 MarkMenuItem(wname, True);
1925 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1927 /* Maybe all the enables should be handled here, not just this one */
1928 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1930 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1935 * Button/menu procedures
1938 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1939 char *selected_fen_position=NULL;
1942 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1943 Atom *type_return, XtPointer *value_return,
1944 unsigned long *length_return, int *format_return)
1946 char *selection_tmp;
1948 // if (!selected_fen_position) return False; /* should never happen */
1949 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1950 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1951 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1954 if (f == NULL) return False;
1958 selection_tmp = XtMalloc(len + 1);
1959 count = fread(selection_tmp, 1, len, f);
1962 XtFree(selection_tmp);
1965 selection_tmp[len] = NULLCHAR;
1967 /* note: since no XtSelectionDoneProc was registered, Xt will
1968 * automatically call XtFree on the value returned. So have to
1969 * make a copy of it allocated with XtMalloc */
1970 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1971 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1974 *value_return=selection_tmp;
1975 *length_return=strlen(selection_tmp);
1976 *type_return=*target;
1977 *format_return = 8; /* bits per byte */
1979 } else if (*target == XA_TARGETS(xDisplay)) {
1980 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1981 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1982 targets_tmp[1] = XA_STRING;
1983 *value_return = targets_tmp;
1984 *type_return = XA_ATOM;
1987 // This code leads to a read of value_return out of bounds on 64-bit systems.
1988 // Other code which I have seen always sets *format_return to 32 independent of
1989 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1990 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1991 *format_return = 8 * sizeof(Atom);
1992 if (*format_return > 32) {
1993 *length_return *= *format_return / 32;
1994 *format_return = 32;
1997 *format_return = 32;
2005 /* note: when called from menu all parameters are NULL, so no clue what the
2006 * Widget which was clicked on was, or what the click event was
2009 CopySomething (char *src)
2011 selected_fen_position = src;
2013 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2014 * have a notion of a position that is selected but not copied.
2015 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2017 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2019 SendPositionSelection,
2020 NULL/* lose_ownership_proc */ ,
2021 NULL/* transfer_done_proc */);
2022 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2024 SendPositionSelection,
2025 NULL/* lose_ownership_proc */ ,
2026 NULL/* transfer_done_proc */);
2029 /* function called when the data to Paste is ready */
2031 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2032 Atom *type, XtPointer value, unsigned long *len, int *format)
2035 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2036 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2037 EditPositionPasteFEN(fenstr);
2041 /* called when Paste Position button is pressed,
2042 * all parameters will be NULL */
2044 PastePositionProc ()
2046 XtGetSelectionValue(menuBarWidget,
2047 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2048 /* (XtSelectionCallbackProc) */ PastePositionCB,
2049 NULL, /* client_data passed to PastePositionCB */
2051 /* better to use the time field from the event that triggered the
2052 * call to this function, but that isn't trivial to get
2059 /* note: when called from menu all parameters are NULL, so no clue what the
2060 * Widget which was clicked on was, or what the click event was
2062 /* function called when the data to Paste is ready */
2064 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2065 Atom *type, XtPointer value, unsigned long *len, int *format)
2068 if (value == NULL || *len == 0) {
2069 return; /* nothing had been selected to copy */
2071 f = fopen(gamePasteFilename, "w");
2073 DisplayError(_("Can't open temp file"), errno);
2076 fwrite(value, 1, *len, f);
2079 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2082 /* called when Paste Game button is pressed,
2083 * all parameters will be NULL */
2087 XtGetSelectionValue(menuBarWidget,
2088 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2089 /* (XtSelectionCallbackProc) */ PasteGameCB,
2090 NULL, /* client_data passed to PasteGameCB */
2092 /* better to use the time field from the event that triggered the
2093 * call to this function, but that isn't trivial to get
2102 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2109 { // bassic primitive for determining if modifier keys are pressed
2110 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2113 XQueryKeymap(xDisplay,keys);
2114 for(i=0; i<6; i++) {
2116 j = XKeysymToKeycode(xDisplay, codes[i]);
2117 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2123 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2127 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2128 if ( n == 1 && *buf >= 32 // printable
2129 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2130 ) BoxAutoPopUp (buf);
2134 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2135 { // [HGM] input: let up-arrow recall previous line from history
2140 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2141 { // [HGM] input: let down-arrow recall next line from history
2146 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2152 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2154 if (!TempBackwardActive) {
2155 TempBackwardActive = True;
2161 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2163 /* Check to see if triggered by a key release event for a repeating key.
2164 * If so the next queued event will be a key press of the same key at the same time */
2165 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2167 XPeekEvent(xDisplay, &next);
2168 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2169 next.xkey.keycode == event->xkey.keycode)
2173 TempBackwardActive = False;
2177 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2178 { // called as key binding
2181 if (nprms && *nprms > 0)
2185 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2191 { // called from menu
2192 ManInner(NULL, NULL, NULL, NULL);
2196 SetWindowTitle (char *text, char *title, char *icon)
2200 if (appData.titleInWindow) {
2202 XtSetArg(args[i], XtNlabel, text); i++;
2203 XtSetValues(titleWidget, args, i);
2206 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2207 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2208 XtSetValues(shellWidget, args, i);
2209 XSync(xDisplay, False);
2214 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2220 DisplayIcsInteractionTitle (String message)
2222 if (oldICSInteractionTitle == NULL) {
2223 /* Magic to find the old window title, adapted from vim */
2224 char *wina = getenv("WINDOWID");
2226 Window win = (Window) atoi(wina);
2227 Window root, parent, *children;
2228 unsigned int nchildren;
2229 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2231 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2232 if (!XQueryTree(xDisplay, win, &root, &parent,
2233 &children, &nchildren)) break;
2234 if (children) XFree((void *)children);
2235 if (parent == root || parent == 0) break;
2238 XSetErrorHandler(oldHandler);
2240 if (oldICSInteractionTitle == NULL) {
2241 oldICSInteractionTitle = "xterm";
2244 printf("\033]0;%s\007", message);
2249 XtIntervalId delayedEventTimerXID = 0;
2250 DelayedEventCallback delayedEventCallback = 0;
2255 delayedEventTimerXID = 0;
2256 delayedEventCallback();
2260 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2262 if(delayedEventTimerXID && delayedEventCallback == cb)
2263 // [HGM] alive: replace, rather than add or flush identical event
2264 XtRemoveTimeOut(delayedEventTimerXID);
2265 delayedEventCallback = cb;
2266 delayedEventTimerXID =
2267 XtAppAddTimeOut(appContext, millisec,
2268 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2271 DelayedEventCallback
2274 if (delayedEventTimerXID) {
2275 return delayedEventCallback;
2282 CancelDelayedEvent ()
2284 if (delayedEventTimerXID) {
2285 XtRemoveTimeOut(delayedEventTimerXID);
2286 delayedEventTimerXID = 0;
2290 XtIntervalId loadGameTimerXID = 0;
2293 LoadGameTimerRunning ()
2295 return loadGameTimerXID != 0;
2299 StopLoadGameTimer ()
2301 if (loadGameTimerXID != 0) {
2302 XtRemoveTimeOut(loadGameTimerXID);
2303 loadGameTimerXID = 0;
2311 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2313 loadGameTimerXID = 0;
2318 StartLoadGameTimer (long millisec)
2321 XtAppAddTimeOut(appContext, millisec,
2322 (XtTimerCallbackProc) LoadGameTimerCallback,
2326 XtIntervalId analysisClockXID = 0;
2329 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2331 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2332 || appData.icsEngineAnalyze) { // [DM]
2333 AnalysisPeriodicEvent(0);
2334 StartAnalysisClock();
2339 StartAnalysisClock ()
2342 XtAppAddTimeOut(appContext, 2000,
2343 (XtTimerCallbackProc) AnalysisClockCallback,
2347 XtIntervalId clockTimerXID = 0;
2350 ClockTimerRunning ()
2352 return clockTimerXID != 0;
2358 if (clockTimerXID != 0) {
2359 XtRemoveTimeOut(clockTimerXID);
2368 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2375 StartClockTimer (long millisec)
2378 XtAppAddTimeOut(appContext, millisec,
2379 (XtTimerCallbackProc) ClockTimerCallback,
2384 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2388 Widget w = (Widget) opt->handle;
2390 /* check for low time warning */
2391 Pixel foregroundOrWarningColor = timerForegroundPixel;
2394 appData.lowTimeWarning &&
2395 (timer / 1000) < appData.icsAlarmTime)
2396 foregroundOrWarningColor = lowTimeWarningColor;
2398 if (appData.clockMode) {
2399 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2400 XtSetArg(args[0], XtNlabel, buf);
2402 snprintf(buf, MSG_SIZ, "%s ", color);
2403 XtSetArg(args[0], XtNlabel, buf);
2408 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2409 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2411 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2412 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2415 XtSetValues(w, args, 3);
2418 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2421 SetClockIcon (int color)
2424 Pixmap pm = *clockIcons[color];
2425 if (iconPixmap != pm) {
2427 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2428 XtSetValues(shellWidget, args, 1);
2432 #define INPUT_SOURCE_BUF_SIZE 8192
2441 char buf[INPUT_SOURCE_BUF_SIZE];
2446 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2448 InputSource *is = (InputSource *) closure;
2453 if (is->lineByLine) {
2454 count = read(is->fd, is->unused,
2455 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2457 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2460 is->unused += count;
2462 while (p < is->unused) {
2463 q = memchr(p, '\n', is->unused - p);
2464 if (q == NULL) break;
2466 (is->func)(is, is->closure, p, q - p, 0);
2470 while (p < is->unused) {
2475 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2480 (is->func)(is, is->closure, is->buf, count, error);
2485 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2488 ChildProc *cp = (ChildProc *) pr;
2490 is = (InputSource *) calloc(1, sizeof(InputSource));
2491 is->lineByLine = lineByLine;
2495 is->fd = fileno(stdin);
2497 is->kind = cp->kind;
2498 is->fd = cp->fdFrom;
2501 is->unused = is->buf;
2504 is->xid = XtAppAddInput(appContext, is->fd,
2505 (XtPointer) (XtInputReadMask),
2506 (XtInputCallbackProc) DoInputCallback,
2508 is->closure = closure;
2509 return (InputSourceRef) is;
2513 RemoveInputSource (InputSourceRef isr)
2515 InputSource *is = (InputSource *) isr;
2517 if (is->xid == 0) return;
2518 XtRemoveInput(is->xid);
2524 static Boolean frameWaiting;
2527 FrameAlarm (int sig)
2529 frameWaiting = False;
2530 /* In case System-V style signals. Needed?? */
2531 signal(SIGALRM, FrameAlarm);
2535 FrameDelay (int time)
2537 struct itimerval delay;
2539 XSync(xDisplay, False);
2542 frameWaiting = True;
2543 signal(SIGALRM, FrameAlarm);
2544 delay.it_interval.tv_sec =
2545 delay.it_value.tv_sec = time / 1000;
2546 delay.it_interval.tv_usec =
2547 delay.it_value.tv_usec = (time % 1000) * 1000;
2548 setitimer(ITIMER_REAL, &delay, NULL);
2549 while (frameWaiting) pause();
2550 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2551 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2552 setitimer(ITIMER_REAL, &delay, NULL);
2559 FrameDelay (int time)
2561 XSync(xDisplay, False);
2563 usleep(time * 1000);
2569 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2571 char buf[MSG_SIZ], *logoName = buf;
2572 if(appData.logo[n][0]) {
2573 logoName = appData.logo[n];
2574 } else if(appData.autoLogo) {
2575 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2576 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2577 } else if(appData.directory[n] && appData.directory[n][0]) {
2578 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2582 { ASSIGN(cps->programLogo, logoName); }
2586 UpdateLogos (int displ)
2588 if(optList[W_WHITE-1].handle == NULL) return;
2589 LoadLogo(&first, 0, 0);
2590 LoadLogo(&second, 1, appData.icsActive);
2591 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);