2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 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"
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
231 int main P((int argc, char **argv));
232 RETSIGTYPE CmailSigHandler P((int sig));
233 RETSIGTYPE IntSigHandler P((int sig));
234 RETSIGTYPE TermSizeSigHandler P((int sig));
235 Widget CreateMenuBar P((Menu *mb, int boardWidth));
237 char *InsertPxlSize P((char *pattern, int targetPxlSize));
238 XFontSet CreateFontSet P((char *base_fnt_lst));
240 char *FindFont P((char *pattern, int targetPxlSize));
242 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
243 u_int wreq, u_int hreq));
244 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
245 void DelayedDrag P((void));
246 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
247 void HandlePV P((Widget w, XEvent * event,
248 String * params, Cardinal * nParams));
249 void DrawPositionProc P((Widget w, XEvent *event,
250 String *prms, Cardinal *nprms));
251 void CommentClick P((Widget w, XEvent * event,
252 String * params, Cardinal * nParams));
253 void ICSInputBoxPopUp P((void));
254 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
255 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
256 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
257 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
258 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
259 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
260 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
261 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
262 Boolean TempBackwardActive = False;
263 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
264 void DisplayMove P((int moveNumber));
265 void update_ics_width P(());
266 int CopyMemoProc P(());
269 * XBoard depends on Xt R4 or higher
271 int xtVersion = XtSpecificationRelease;
276 Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
277 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
278 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
279 Option *optList; // contains all widgets of main window
281 XFontSet fontSet, clockFontSet;
284 XFontStruct *clockFontStruct;
286 Font coordFontID, countFontID;
287 XFontStruct *coordFontStruct, *countFontStruct;
288 XtAppContext appContext;
291 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
293 Position commentX = -1, commentY = -1;
294 Dimension commentW, commentH;
295 typedef unsigned int BoardSize;
297 Boolean chessProgram;
299 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
300 int smallLayout = 0, tinyLayout = 0,
301 marginW, marginH, // [HGM] for run-time resizing
302 fromX = -1, fromY = -1, toX, toY, commentUp = False,
303 errorExitStatus = -1, defaultLineGap;
304 Dimension textHeight;
305 Pixel timerForegroundPixel, timerBackgroundPixel;
306 Pixel buttonForegroundPixel, buttonBackgroundPixel;
307 char *chessDir, *programName, *programVersion;
308 Boolean alwaysOnTop = False;
309 char *icsTextMenuString;
311 char *firstChessProgramNames;
312 char *secondChessProgramNames;
314 WindowPlacement wpMain;
315 WindowPlacement wpConsole;
316 WindowPlacement wpComment;
317 WindowPlacement wpMoveHistory;
318 WindowPlacement wpEvalGraph;
319 WindowPlacement wpEngineOutput;
320 WindowPlacement wpGameList;
321 WindowPlacement wpTags;
322 WindowPlacement wpDualBoard;
325 /* This magic number is the number of intermediate frames used
326 in each half of the animation. For short moves it's reduced
327 by 1. The total number of frames will be factor * 2 + 1. */
330 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
337 DropMenuEnables dmEnables[] = {
354 XtResource clientResources[] = {
355 { "flashCount", "flashCount", XtRInt, sizeof(int),
356 XtOffset(AppDataPtr, flashCount), XtRImmediate,
357 (XtPointer) FLASH_COUNT },
360 XrmOptionDescRec shellOptions[] = {
361 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
362 { "-flash", "flashCount", XrmoptionNoArg, "3" },
363 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
366 XtActionsRec boardActions[] = {
367 { "DrawPosition", DrawPositionProc },
368 { "HandlePV", HandlePV },
369 { "SelectPV", SelectPV },
370 { "StopPV", StopPV },
371 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
372 { "QuitProc", QuitWrapper },
373 { "ManProc", ManInner },
374 { "TempBackwardProc", TempBackwardProc },
375 { "TempForwardProc", TempForwardProc },
376 { "CommentClick", (XtActionProc) CommentClick },
377 { "GenericPopDown", (XtActionProc) GenericPopDown },
378 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
379 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
380 { "SelectMove", (XtActionProc) SelectMoveX },
381 { "LoadSelectedProc", LoadSelectedProc },
382 { "SetFilterProc", SetFilterProc },
383 { "TypeInProc", TypeInProc },
384 { "EnterKeyProc", EnterKeyProc },
385 { "UpKeyProc", UpKeyProc },
386 { "DownKeyProc", DownKeyProc },
387 { "WheelProc", WheelProc },
388 { "TabProc", TabProc },
391 char globalTranslations[] =
392 ":Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
393 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
394 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
395 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
396 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
397 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
398 :<Key>Pause: MenuItem(Mode.Pause) \n \
399 :Ctrl<Key>d: MenuItem(DebugProc) \n \
400 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
401 :<Key>Left: MenuItem(Edit.Backward) \n \
402 :<Key>Right: MenuItem(Edit.Forward) \n \
403 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
404 #ifndef OPTIONSDIALOG
406 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
407 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
408 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
409 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
410 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
413 :<KeyDown>Return: TempBackwardProc() \n \
414 :<KeyUp>Return: TempForwardProc() \n";
416 char ICSInputTranslations[] =
417 "<Key>Up: UpKeyProc() \n "
418 "<Key>Down: DownKeyProc() \n "
419 "<Key>Return: EnterKeyProc() \n";
421 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
422 // as the widget is destroyed before the up-click can call extend-end
423 char commentTranslations[] = "<Btn3Down>: extend-end(PRIMARY) select-start() CommentClick() \n";
425 String xboardResources[] = {
426 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
431 /* Max possible square size */
432 #define MAXSQSIZE 256
434 static int xpm_avail[MAXSQSIZE];
436 #ifdef HAVE_DIR_STRUCT
438 /* Extract piece size from filename */
440 xpm_getsize (char *name, int len, char *ext)
448 if ((p=strchr(name, '.')) == NULL ||
449 StrCaseCmp(p+1, ext) != 0)
455 while (*p && isdigit(*p))
462 /* Setup xpm_avail */
464 xpm_getavail (char *dirname, char *ext)
470 for (i=0; i<MAXSQSIZE; ++i)
473 if (appData.debugMode)
474 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
476 dir = opendir(dirname);
479 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
480 programName, dirname);
484 while ((ent=readdir(dir)) != NULL) {
485 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
486 if (i > 0 && i < MAXSQSIZE)
496 xpm_print_avail (FILE *fp, char *ext)
500 fprintf(fp, _("Available `%s' sizes:\n"), ext);
501 for (i=1; i<MAXSQSIZE; ++i) {
507 /* Return XPM piecesize closest to size */
509 xpm_closest_to (char *dirname, int size, char *ext)
512 int sm_diff = MAXSQSIZE;
516 xpm_getavail(dirname, ext);
518 if (appData.debugMode)
519 xpm_print_avail(stderr, ext);
521 for (i=1; i<MAXSQSIZE; ++i) {
524 diff = (diff<0) ? -diff : diff;
525 if (diff < sm_diff) {
533 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
539 #else /* !HAVE_DIR_STRUCT */
540 /* If we are on a system without a DIR struct, we can't
541 read the directory, so we can't collect a list of
542 filenames, etc., so we can't do any size-fitting. */
544 xpm_closest_to (char *dirname, int size, char *ext)
547 Warning: No DIR structure found on this system --\n\
548 Unable to autosize for XPM/XIM pieces.\n\
549 Please report this error to %s.\n\
550 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
553 #endif /* HAVE_DIR_STRUCT */
556 /* Arrange to catch delete-window events */
557 Atom wm_delete_window;
559 CatchDeleteWindow (Widget w, String procname)
562 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
563 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
564 XtAugmentTranslations(w, XtParseTranslationTable(buf));
571 XtSetArg(args[0], XtNiconic, False);
572 XtSetValues(shellWidget, args, 1);
574 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
577 //---------------------------------------------------------------------------------------------------------
578 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
581 #define CW_USEDEFAULT (1<<31)
582 #define ICS_TEXT_MENU_SIZE 90
583 #define DEBUG_FILE "xboard.debug"
584 #define SetCurrentDirectory chdir
585 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
589 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
592 // front-end part of option handling
594 // [HGM] This platform-dependent table provides the location for storing the color info
595 extern char *crWhite, * crBlack;
599 &appData.whitePieceColor,
600 &appData.blackPieceColor,
601 &appData.lightSquareColor,
602 &appData.darkSquareColor,
603 &appData.highlightSquareColor,
604 &appData.premoveHighlightColor,
605 &appData.lowTimeWarningColor,
616 // [HGM] font: keep a font for each square size, even non-stndard ones
619 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
620 char *fontTable[NUM_FONTS][MAX_SIZE];
623 ParseFont (char *name, int number)
624 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
626 if(sscanf(name, "size%d:", &size)) {
627 // [HGM] font: font is meant for specific boardSize (likely from settings file);
628 // defer processing it until we know if it matches our board size
629 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
630 fontTable[number][size] = strdup(strchr(name, ':')+1);
631 fontValid[number][size] = True;
636 case 0: // CLOCK_FONT
637 appData.clockFont = strdup(name);
639 case 1: // MESSAGE_FONT
640 appData.font = strdup(name);
642 case 2: // COORD_FONT
643 appData.coordFont = strdup(name);
648 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
653 { // only 2 fonts currently
654 appData.clockFont = CLOCK_FONT_NAME;
655 appData.coordFont = COORD_FONT_NAME;
656 appData.font = DEFAULT_FONT_NAME;
661 { // no-op, until we identify the code for this already in XBoard and move it here
665 ParseColor (int n, char *name)
666 { // in XBoard, just copy the color-name string
667 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
671 ParseTextAttribs (ColorClass cc, char *s)
673 (&appData.colorShout)[cc] = strdup(s);
677 ParseBoardSize (void *addr, char *name)
679 appData.boardSize = strdup(name);
684 { // In XBoard the sound-playing program takes care of obtaining the actual sound
688 SetCommPortDefaults ()
689 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
692 // [HGM] args: these three cases taken out to stay in front-end
694 SaveFontArg (FILE *f, ArgDescriptor *ad)
697 int i, n = (int)(intptr_t)ad->argLoc;
699 case 0: // CLOCK_FONT
700 name = appData.clockFont;
702 case 1: // MESSAGE_FONT
705 case 2: // COORD_FONT
706 name = appData.coordFont;
711 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
712 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
713 fontTable[n][squareSize] = strdup(name);
714 fontValid[n][squareSize] = True;
717 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
718 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
723 { // nothing to do, as the sounds are at all times represented by their text-string names already
727 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
728 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
729 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
733 SaveColor (FILE *f, ArgDescriptor *ad)
734 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
735 if(colorVariable[(int)(intptr_t)ad->argLoc])
736 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
740 SaveBoardSize (FILE *f, char *name, void *addr)
741 { // wrapper to shield back-end from BoardSize & sizeInfo
742 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
746 ParseCommPortSettings (char *s)
747 { // no such option in XBoard (yet)
753 GetActualPlacement (Widget wg, WindowPlacement *wp)
755 XWindowAttributes winAt;
762 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
763 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
764 wp->x = rx - winAt.x;
765 wp->y = ry - winAt.y;
766 wp->height = winAt.height;
767 wp->width = winAt.width;
768 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
773 { // wrapper to shield use of window handles from back-end (make addressible by number?)
774 // In XBoard this will have to wait until awareness of window parameters is implemented
775 GetActualPlacement(shellWidget, &wpMain);
776 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
777 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
778 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
779 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
780 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
781 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
785 PrintCommPortSettings (FILE *f, char *name)
786 { // This option does not exist in XBoard
790 EnsureOnScreen (int *x, int *y, int minX, int minY)
797 { // [HGM] args: allows testing if main window is realized from back-end
798 return xBoardWindow != 0;
802 PopUpStartupDialog ()
803 { // start menu not implemented in XBoard
807 ConvertToLine (int argc, char **argv)
809 static char line[128*1024], buf[1024];
813 for(i=1; i<argc; i++)
815 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
816 && argv[i][0] != '{' )
817 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
819 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
820 strncat(line, buf, 128*1024 - strlen(line) - 1 );
823 line[strlen(line)-1] = NULLCHAR;
827 //--------------------------------------------------------------------------------------------
830 ResizeBoardWindow (int w, int h, int inhibit)
832 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
834 shellArgs[0].value = w;
835 shellArgs[1].value = h;
836 shellArgs[4].value = shellArgs[2].value = w;
837 shellArgs[5].value = shellArgs[3].value = h;
838 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
840 XSync(xDisplay, False);
844 MakeOneColor (char *name, Pixel *color)
847 if (!appData.monoMode) {
848 vFrom.addr = (caddr_t) name;
849 vFrom.size = strlen(name);
850 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
851 if (vTo.addr == NULL) {
852 appData.monoMode = True;
855 *color = *(Pixel *) vTo.addr;
863 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
864 int forceMono = False;
866 if (appData.lowTimeWarning)
867 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
868 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
869 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
875 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
876 { // detervtomine what fonts to use, and create them
880 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
881 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
882 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
883 appData.font = fontTable[MESSAGE_FONT][squareSize];
884 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
885 appData.coordFont = fontTable[COORD_FONT][squareSize];
888 appData.font = InsertPxlSize(appData.font, fontPxlSize);
889 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
890 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
891 fontSet = CreateFontSet(appData.font);
892 clockFontSet = CreateFontSet(appData.clockFont);
894 /* For the coordFont, use the 0th font of the fontset. */
895 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
896 XFontStruct **font_struct_list;
897 XFontSetExtents *fontSize;
898 char **font_name_list;
899 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
900 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
901 coordFontStruct = XQueryFont(xDisplay, coordFontID);
902 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
903 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
906 appData.font = FindFont(appData.font, fontPxlSize);
907 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
908 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
909 clockFontID = XLoadFont(xDisplay, appData.clockFont);
910 clockFontStruct = XQueryFont(xDisplay, clockFontID);
911 coordFontID = XLoadFont(xDisplay, appData.coordFont);
912 coordFontStruct = XQueryFont(xDisplay, coordFontID);
913 // textHeight in !NLS mode!
915 countFontID = coordFontID; // [HGM] holdings
916 countFontStruct = coordFontStruct;
918 xdb = XtDatabase(xDisplay);
920 XrmPutLineResource(&xdb, "*international: True");
921 vTo.size = sizeof(XFontSet);
922 vTo.addr = (XtPointer) &fontSet;
923 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
925 XrmPutStringResource(&xdb, "*font", appData.font);
935 case ArgInt: p = " N"; break;
936 case ArgString: p = " STR"; break;
937 case ArgBoolean: p = " TF"; break;
938 case ArgSettingsFilename:
939 case ArgBackupSettingsFile:
940 case ArgFilename: p = " FILE"; break;
941 case ArgX: p = " Nx"; break;
942 case ArgY: p = " Ny"; break;
943 case ArgAttribs: p = " TEXTCOL"; break;
944 case ArgColor: p = " COL"; break;
945 case ArgFont: p = " FONT"; break;
946 case ArgBoardSize: p = " SIZE"; break;
947 case ArgFloat: p = " FLOAT"; break;
952 case ArgCommSettings:
959 GenerateGlobalTranslationTable (void)
961 /* go through all menu items and extract the keyboard shortcuts, so that X11 can load them */
969 /* loop over all menu entries */
970 for( i=0; menuBar[i].mi ; i++)
973 for(j=0; mi[j].proc; j++)
981 char *key,*test, *mods;
983 /* check for Ctrl/Alt */
984 if( strstr(mi[j].accel, "<Ctrl>") ) ctrl = 1;
985 if( strstr(mi[j].accel, "<Shift>") ) shift = 1;
986 if( strstr(mi[j].accel, "<Alt>") ) alt = 1;
988 /* remove all <...> */
989 test = strrchr(mi[j].accel, '>');
991 key = strdup(mi[j].accel);
993 key = strdup(++test); // remove ">"
995 /* instead of shift X11 uses the uppercase letter directly*/
996 if (shift && strlen(key)==1 )
998 *key = toupper(*key);
1002 /* handle some special cases which have different names in X11 */
1003 if ( strncmp(key, "Page_Down", 9) == 0 )
1008 else if ( strncmp(key, "Page_Up", 7) == 0 )
1011 key=strdup("Prior");
1014 /* create string of mods */
1016 mods = strdup("Ctrl ");
1022 mods = realloc(mods, strlen(mods) + strlen("Meta ")+1);
1023 strncat(mods, "Meta ", 5);
1028 mods = realloc(mods, strlen(mods) + strlen("Shift ")+1);
1029 strncat(mods, "Shift ", 6);
1032 // remove trailing space
1033 if( isspace(mods[strlen(mods)-1]) )
1034 mods[strlen(mods)-1]='\0';
1036 /* get the name for the callback, we can use MenuItem() here that will call KeyBindingProc */
1037 size_t namesize = snprintf(NULL, 0, "%s.%s", menuBar[i].ref, mi[j].ref);
1038 char *name = malloc(namesize+1);
1039 snprintf(name, namesize+1, "%s.%s", menuBar[i].ref, mi[j].ref);
1041 size_t buffersize = snprintf(NULL, 0, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
1042 char *buffer = malloc(buffersize+1);
1043 snprintf(buffer, buffersize+1, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
1045 /* add string to the output */
1046 output = realloc(output, strlen(output) + strlen(buffer)+1);
1047 strncat(output, buffer, strlen(buffer));
1066 ArgDescriptor *q, *p = argDescriptors+5;
1067 printf("\nXBoard accepts the following options:\n"
1068 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1069 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1070 " SIZE = board-size spec(s)\n"
1071 " Within parentheses are short forms, or options to set to true or false.\n"
1072 " Persistent options (saved in the settings file) are marked with *)\n\n");
1074 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1075 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1076 if(p->save) strcat(buf+len, "*");
1077 for(q=p+1; q->argLoc == p->argLoc; q++) {
1078 if(q->argName[0] == '-') continue;
1079 strcat(buf+len, q == p+1 ? " (" : " ");
1080 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1082 if(q != p+1) strcat(buf+len, ")");
1084 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1087 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1091 SlaveResize (Option *opt)
1096 main (int argc, char **argv)
1098 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1099 XSetWindowAttributes window_attributes;
1101 Dimension boardWidth, boardHeight, w, h;
1103 int forceMono = False;
1105 srandom(time(0)); // [HGM] book: make random truly random
1107 setbuf(stdout, NULL);
1108 setbuf(stderr, NULL);
1111 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1112 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1116 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1121 programName = strrchr(argv[0], '/');
1122 if (programName == NULL)
1123 programName = argv[0];
1128 XtSetLanguageProc(NULL, NULL, NULL);
1129 if (appData.debugMode) {
1130 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1133 bindtextdomain(PACKAGE, LOCALEDIR);
1134 textdomain(PACKAGE);
1137 appData.boardSize = "";
1138 InitAppData(ConvertToLine(argc, argv));
1140 if (p == NULL) p = "/tmp";
1141 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1142 gameCopyFilename = (char*) malloc(i);
1143 gamePasteFilename = (char*) malloc(i);
1144 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1145 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1147 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1148 static char buf[MSG_SIZ];
1149 EscapeExpand(buf, appData.firstInitString);
1150 appData.firstInitString = strdup(buf);
1151 EscapeExpand(buf, appData.secondInitString);
1152 appData.secondInitString = strdup(buf);
1153 EscapeExpand(buf, appData.firstComputerString);
1154 appData.firstComputerString = strdup(buf);
1155 EscapeExpand(buf, appData.secondComputerString);
1156 appData.secondComputerString = strdup(buf);
1159 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1162 if (chdir(chessDir) != 0) {
1163 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1169 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1170 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1171 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1172 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1175 setbuf(debugFP, NULL);
1178 /* [HGM,HR] make sure board size is acceptable */
1179 if(appData.NrFiles > BOARD_FILES ||
1180 appData.NrRanks > BOARD_RANKS )
1181 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1184 /* This feature does not work; animation needs a rewrite */
1185 appData.highlightDragging = FALSE;
1189 gameInfo.variant = StringToVariant(appData.variant);
1190 InitPosition(FALSE);
1193 XtAppInitialize(&appContext, "XBoard", shellOptions,
1194 XtNumber(shellOptions),
1195 &argc, argv, xboardResources, NULL, 0);
1197 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1198 clientResources, XtNumber(clientResources),
1201 xDisplay = XtDisplay(shellWidget);
1202 xScreen = DefaultScreen(xDisplay);
1203 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1206 * determine size, based on supplied or remembered -size, or screen size
1208 if (isdigit(appData.boardSize[0])) {
1209 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1210 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1211 &fontPxlSize, &smallLayout, &tinyLayout);
1213 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1214 programName, appData.boardSize);
1218 /* Find some defaults; use the nearest known size */
1219 SizeDefaults *szd, *nearest;
1220 int distance = 99999;
1221 nearest = szd = sizeDefaults;
1222 while (szd->name != NULL) {
1223 if (abs(szd->squareSize - squareSize) < distance) {
1225 distance = abs(szd->squareSize - squareSize);
1226 if (distance == 0) break;
1230 if (i < 2) lineGap = nearest->lineGap;
1231 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1232 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1233 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1234 if (i < 6) smallLayout = nearest->smallLayout;
1235 if (i < 7) tinyLayout = nearest->tinyLayout;
1238 SizeDefaults *szd = sizeDefaults;
1239 if (*appData.boardSize == NULLCHAR) {
1240 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1241 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1244 if (szd->name == NULL) szd--;
1245 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1247 while (szd->name != NULL &&
1248 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1249 if (szd->name == NULL) {
1250 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1251 programName, appData.boardSize);
1255 squareSize = szd->squareSize;
1256 lineGap = szd->lineGap;
1257 clockFontPxlSize = szd->clockFontPxlSize;
1258 coordFontPxlSize = szd->coordFontPxlSize;
1259 fontPxlSize = szd->fontPxlSize;
1260 smallLayout = szd->smallLayout;
1261 tinyLayout = szd->tinyLayout;
1262 // [HGM] font: use defaults from settings file if available and not overruled
1265 defaultLineGap = lineGap;
1266 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1268 /* [HR] height treated separately (hacked) */
1269 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1270 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1273 * Determine what fonts to use.
1275 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1278 * Detect if there are not enough colors available and adapt.
1280 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1281 appData.monoMode = True;
1284 forceMono = MakeColors();
1287 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1289 appData.monoMode = True;
1292 if (appData.monoMode && appData.debugMode) {
1293 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1294 (unsigned long) XWhitePixel(xDisplay, xScreen),
1295 (unsigned long) XBlackPixel(xDisplay, xScreen));
1298 ParseIcsTextColors();
1300 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1306 layoutName = "tinyLayout";
1307 } else if (smallLayout) {
1308 layoutName = "smallLayout";
1310 layoutName = "normalLayout";
1313 optList = BoardPopUp(squareSize, lineGap, (void*)
1319 InitDrawingHandle(optList + W_BOARD);
1320 currBoard = &optList[W_BOARD];
1321 boardWidget = optList[W_BOARD].handle;
1322 menuBarWidget = optList[W_MENU].handle;
1323 dropMenu = optList[W_DROP].handle;
1324 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1325 formWidget = XtParent(boardWidget);
1326 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1327 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1328 XtGetValues(optList[W_WHITE].handle, args, 2);
1329 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1330 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1331 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1332 XtGetValues(optList[W_PAUSE].handle, args, 2);
1335 xBoardWindow = XtWindow(boardWidget);
1337 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1338 // not need to go into InitDrawingSizes().
1341 * Create X checkmark bitmap and initialize option menu checks.
1343 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1344 checkmark_bits, checkmark_width, checkmark_height);
1350 ReadBitmap(&wIconPixmap, "icon_white.bm",
1351 icon_white_bits, icon_white_width, icon_white_height);
1352 ReadBitmap(&bIconPixmap, "icon_black.bm",
1353 icon_black_bits, icon_black_width, icon_black_height);
1354 iconPixmap = wIconPixmap;
1356 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1357 XtSetValues(shellWidget, args, i);
1360 * Create a cursor for the board widget.
1362 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1363 XChangeWindowAttributes(xDisplay, xBoardWindow,
1364 CWCursor, &window_attributes);
1367 * Inhibit shell resizing.
1369 shellArgs[0].value = (XtArgVal) &w;
1370 shellArgs[1].value = (XtArgVal) &h;
1371 XtGetValues(shellWidget, shellArgs, 2);
1372 shellArgs[4].value = shellArgs[2].value = w;
1373 shellArgs[5].value = shellArgs[3].value = h;
1374 // XtSetValues(shellWidget, &shellArgs[2], 4);
1375 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1376 marginH = h - boardHeight;
1378 CatchDeleteWindow(shellWidget, "QuitProc");
1383 if(appData.logoSize)
1384 { // locate and read user logo
1386 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1387 ASSIGN(userLogo, buf);
1390 if (appData.animate || appData.animateDragging)
1394 char *TranslationsTableMenus=GenerateGlobalTranslationTable ();
1396 XtAugmentTranslations(formWidget,
1397 XtParseTranslationTable(globalTranslations));
1398 XtAugmentTranslations(formWidget,
1399 XtParseTranslationTable(TranslationsTableMenus));
1401 XtAddEventHandler(formWidget, KeyPressMask, False,
1402 (XtEventHandler) MoveTypeInProc, NULL);
1403 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1404 (XtEventHandler) EventProc, NULL);
1406 /* [AS] Restore layout */
1407 if( wpMoveHistory.visible ) {
1411 if( wpEvalGraph.visible )
1416 if( wpEngineOutput.visible ) {
1417 EngineOutputPopUp();
1422 if (errorExitStatus == -1) {
1423 if (appData.icsActive) {
1424 /* We now wait until we see "login:" from the ICS before
1425 sending the logon script (problems with timestamp otherwise) */
1426 /*ICSInitScript();*/
1427 if (appData.icsInputBox) ICSInputBoxPopUp();
1431 signal(SIGWINCH, TermSizeSigHandler);
1433 signal(SIGINT, IntSigHandler);
1434 signal(SIGTERM, IntSigHandler);
1435 if (*appData.cmailGameName != NULLCHAR) {
1436 signal(SIGUSR1, CmailSigHandler);
1440 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1443 // XtSetKeyboardFocus(shellWidget, formWidget);
1444 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1446 XtAppMainLoop(appContext);
1447 if (appData.debugMode) fclose(debugFP); // [DM] debug
1452 TermSizeSigHandler (int sig)
1458 IntSigHandler (int sig)
1464 CmailSigHandler (int sig)
1469 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1471 /* Activate call-back function CmailSigHandlerCallBack() */
1472 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1474 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1478 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1481 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1483 /**** end signal code ****/
1486 #define Abs(n) ((n)<0 ? -(n) : (n))
1490 InsertPxlSize (char *pattern, int targetPxlSize)
1492 char *base_fnt_lst, strInt[12], *p, *q;
1493 int alternatives, i, len, strIntLen;
1496 * Replace the "*" (if present) in the pixel-size slot of each
1497 * alternative with the targetPxlSize.
1501 while ((p = strchr(p, ',')) != NULL) {
1505 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1506 strIntLen = strlen(strInt);
1507 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1511 while (alternatives--) {
1512 char *comma = strchr(p, ',');
1513 for (i=0; i<14; i++) {
1514 char *hyphen = strchr(p, '-');
1516 if (comma && hyphen > comma) break;
1517 len = hyphen + 1 - p;
1518 if (i == 7 && *p == '*' && len == 2) {
1520 memcpy(q, strInt, strIntLen);
1530 len = comma + 1 - p;
1537 return base_fnt_lst;
1541 CreateFontSet (char *base_fnt_lst)
1544 char **missing_list;
1548 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1549 &missing_list, &missing_count, &def_string);
1550 if (appData.debugMode) {
1552 XFontStruct **font_struct_list;
1553 char **font_name_list;
1554 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1556 fprintf(debugFP, " got list %s, locale %s\n",
1557 XBaseFontNameListOfFontSet(fntSet),
1558 XLocaleOfFontSet(fntSet));
1559 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1560 for (i = 0; i < count; i++) {
1561 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1564 for (i = 0; i < missing_count; i++) {
1565 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1568 if (fntSet == NULL) {
1569 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1574 #else // not ENABLE_NLS
1576 * Find a font that matches "pattern" that is as close as
1577 * possible to the targetPxlSize. Prefer fonts that are k
1578 * pixels smaller to fonts that are k pixels larger. The
1579 * pattern must be in the X Consortium standard format,
1580 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1581 * The return value should be freed with XtFree when no
1585 FindFont (char *pattern, int targetPxlSize)
1587 char **fonts, *p, *best, *scalable, *scalableTail;
1588 int i, j, nfonts, minerr, err, pxlSize;
1590 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1592 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1593 programName, pattern);
1600 for (i=0; i<nfonts; i++) {
1603 if (*p != '-') continue;
1605 if (*p == NULLCHAR) break;
1606 if (*p++ == '-') j++;
1608 if (j < 7) continue;
1611 scalable = fonts[i];
1614 err = pxlSize - targetPxlSize;
1615 if (Abs(err) < Abs(minerr) ||
1616 (minerr > 0 && err < 0 && -err == minerr)) {
1622 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1623 /* If the error is too big and there is a scalable font,
1624 use the scalable font. */
1625 int headlen = scalableTail - scalable;
1626 p = (char *) XtMalloc(strlen(scalable) + 10);
1627 while (isdigit(*scalableTail)) scalableTail++;
1628 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1630 p = (char *) XtMalloc(strlen(best) + 2);
1631 safeStrCpy(p, best, strlen(best)+1 );
1633 if (appData.debugMode) {
1634 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1635 pattern, targetPxlSize, p);
1637 XFreeFontNames(fonts);
1643 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1646 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1652 MarkMenuItem (char *menuRef, int state)
1654 MenuItem *item = MenuNameToItem(menuRef);
1658 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1659 XtSetValues(item->handle, args, 1);
1664 EnableNamedMenuItem (char *menuRef, int state)
1666 MenuItem *item = MenuNameToItem(menuRef);
1668 if(item) XtSetSensitive(item->handle, state);
1672 EnableButtonBar (int state)
1674 XtSetSensitive(optList[W_BUTTON].handle, state);
1679 SetMenuEnables (Enables *enab)
1681 while (enab->name != NULL) {
1682 EnableNamedMenuItem(enab->name, enab->value);
1688 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1689 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1691 if(*nprms == 0) return;
1692 item = MenuNameToItem(prms[0]);
1693 if(item) ((MenuProc *) item->proc) ();
1705 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1706 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1707 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1708 dmEnables[i].piece);
1709 XtSetSensitive(entry, p != NULL || !appData.testLegality
1710 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1711 && !appData.icsActive));
1713 while (p && *p++ == dmEnables[i].piece) count++;
1714 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1716 XtSetArg(args[j], XtNlabel, label); j++;
1717 XtSetValues(entry, args, j);
1722 do_flash_delay (unsigned long msec)
1728 FlashDelay (int flash_delay)
1730 XSync(xDisplay, False);
1731 if(flash_delay) do_flash_delay(flash_delay);
1735 Fraction (int x, int start, int stop)
1737 double f = ((double) x - start)/(stop - start);
1738 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1742 static WindowPlacement wpNew;
1745 CoDrag (Widget sh, WindowPlacement *wp)
1748 int j=0, touch=0, fudge = 2;
1749 GetActualPlacement(sh, wp);
1750 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1751 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1752 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1753 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1754 if(!touch ) return; // only windows that touch co-move
1755 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1756 int heightInc = wpNew.height - wpMain.height;
1757 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1758 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1759 wp->y += fracTop * heightInc;
1760 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1761 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1762 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1763 int widthInc = wpNew.width - wpMain.width;
1764 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1765 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1766 wp->y += fracLeft * widthInc;
1767 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1768 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1770 wp->x += wpNew.x - wpMain.x;
1771 wp->y += wpNew.y - wpMain.y;
1772 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1773 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1774 XtSetArg(args[j], XtNx, wp->x); j++;
1775 XtSetArg(args[j], XtNy, wp->y); j++;
1776 XtSetValues(sh, args, j);
1780 ReSize (WindowPlacement *wp)
1783 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1784 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1785 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1786 if(sqy < sqx) sqx = sqy;
1787 if(sqx != squareSize) {
1788 squareSize = sqx; // adopt new square size
1789 CreatePNGPieces(); // make newly scaled pieces
1790 InitDrawingSizes(0, 0); // creates grid etc.
1791 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1792 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1793 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1794 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1795 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1798 static XtIntervalId delayedDragID = 0;
1807 GetActualPlacement(shellWidget, &wpNew);
1808 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1809 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1810 busy = 0; return; // false alarm
1813 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1814 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1815 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1816 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1818 DrawPosition(True, NULL);
1819 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1827 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1829 XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1833 EventProc (Widget widget, caddr_t unused, XEvent *event)
1835 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1836 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1840 * event handler for redrawing the board
1843 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1845 DrawPosition(True, NULL);
1850 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1851 { // [HGM] pv: walk PV
1852 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1855 extern int savedIndex; /* gross that this is global */
1858 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1861 XawTextPosition index, dummy;
1864 XawTextGetSelectionPos(w, &index, &dummy);
1865 XtSetArg(arg, XtNstring, &val);
1866 XtGetValues(w, &arg, 1);
1867 ReplaceComment(savedIndex, val);
1868 if(savedIndex != currentMove) ToNrEvent(savedIndex);
1869 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1873 /* Disable all user input other than deleting the window */
1874 static int frozen = 0;
1880 /* Grab by a widget that doesn't accept input */
1881 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1885 /* Undo a FreezeUI */
1889 if (!frozen) return;
1890 XtRemoveGrab(optList[W_MESSG].handle);
1898 static int oldPausing = FALSE;
1899 static GameMode oldmode = (GameMode) -1;
1902 if (!boardWidget || !XtIsRealized(boardWidget)) return;
1904 if (pausing != oldPausing) {
1905 oldPausing = pausing;
1906 MarkMenuItem("Mode.Pause", pausing);
1908 if (appData.showButtonBar) {
1909 /* Always toggle, don't set. Previous code messes up when
1910 invoked while the button is pressed, as releasing it
1911 toggles the state again. */
1914 XtSetArg(args[0], XtNbackground, &oldbg);
1915 XtSetArg(args[1], XtNforeground, &oldfg);
1916 XtGetValues(optList[W_PAUSE].handle,
1918 XtSetArg(args[0], XtNbackground, oldfg);
1919 XtSetArg(args[1], XtNforeground, oldbg);
1921 XtSetValues(optList[W_PAUSE].handle, args, 2);
1925 wname = ModeToWidgetName(oldmode);
1926 if (wname != NULL) {
1927 MarkMenuItem(wname, False);
1929 wname = ModeToWidgetName(gameMode);
1930 if (wname != NULL) {
1931 MarkMenuItem(wname, True);
1934 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1936 /* Maybe all the enables should be handled here, not just this one */
1937 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1939 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1944 * Button/menu procedures
1947 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1948 char *selected_fen_position=NULL;
1951 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1952 Atom *type_return, XtPointer *value_return,
1953 unsigned long *length_return, int *format_return)
1955 char *selection_tmp;
1957 // if (!selected_fen_position) return False; /* should never happen */
1958 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1959 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1960 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1963 if (f == NULL) return False;
1967 selection_tmp = XtMalloc(len + 1);
1968 count = fread(selection_tmp, 1, len, f);
1971 XtFree(selection_tmp);
1974 selection_tmp[len] = NULLCHAR;
1976 /* note: since no XtSelectionDoneProc was registered, Xt will
1977 * automatically call XtFree on the value returned. So have to
1978 * make a copy of it allocated with XtMalloc */
1979 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1980 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1983 *value_return=selection_tmp;
1984 *length_return=strlen(selection_tmp);
1985 *type_return=*target;
1986 *format_return = 8; /* bits per byte */
1988 } else if (*target == XA_TARGETS(xDisplay)) {
1989 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1990 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1991 targets_tmp[1] = XA_STRING;
1992 *value_return = targets_tmp;
1993 *type_return = XA_ATOM;
1996 // This code leads to a read of value_return out of bounds on 64-bit systems.
1997 // Other code which I have seen always sets *format_return to 32 independent of
1998 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1999 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
2000 *format_return = 8 * sizeof(Atom);
2001 if (*format_return > 32) {
2002 *length_return *= *format_return / 32;
2003 *format_return = 32;
2006 *format_return = 32;
2014 /* note: when called from menu all parameters are NULL, so no clue what the
2015 * Widget which was clicked on was, or what the click event was
2018 CopySomething (char *src)
2020 selected_fen_position = src;
2022 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2023 * have a notion of a position that is selected but not copied.
2024 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2026 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2028 SendPositionSelection,
2029 NULL/* lose_ownership_proc */ ,
2030 NULL/* transfer_done_proc */);
2031 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2033 SendPositionSelection,
2034 NULL/* lose_ownership_proc */ ,
2035 NULL/* transfer_done_proc */);
2038 /* function called when the data to Paste is ready */
2040 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2041 Atom *type, XtPointer value, unsigned long *len, int *format)
2044 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2045 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2046 EditPositionPasteFEN(fenstr);
2050 /* called when Paste Position button is pressed,
2051 * all parameters will be NULL */
2053 PastePositionProc ()
2055 XtGetSelectionValue(menuBarWidget,
2056 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2057 /* (XtSelectionCallbackProc) */ PastePositionCB,
2058 NULL, /* client_data passed to PastePositionCB */
2060 /* better to use the time field from the event that triggered the
2061 * call to this function, but that isn't trivial to get
2068 /* note: when called from menu all parameters are NULL, so no clue what the
2069 * Widget which was clicked on was, or what the click event was
2071 /* function called when the data to Paste is ready */
2073 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2074 Atom *type, XtPointer value, unsigned long *len, int *format)
2077 if (value == NULL || *len == 0) {
2078 return; /* nothing had been selected to copy */
2080 f = fopen(gamePasteFilename, "w");
2082 DisplayError(_("Can't open temp file"), errno);
2085 fwrite(value, 1, *len, f);
2088 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2091 /* called when Paste Game button is pressed,
2092 * all parameters will be NULL */
2096 XtGetSelectionValue(menuBarWidget,
2097 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2098 /* (XtSelectionCallbackProc) */ PasteGameCB,
2099 NULL, /* client_data passed to PasteGameCB */
2101 /* better to use the time field from the event that triggered the
2102 * call to this function, but that isn't trivial to get
2111 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2118 { // bassic primitive for determining if modifier keys are pressed
2119 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2122 XQueryKeymap(xDisplay,keys);
2123 for(i=0; i<6; i++) {
2125 j = XKeysymToKeycode(xDisplay, codes[i]);
2126 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2132 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2136 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2137 if ( n == 1 && *buf >= 32 // printable
2138 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2139 ) BoxAutoPopUp (buf);
2143 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2144 { // [HGM] input: let up-arrow recall previous line from history
2149 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2150 { // [HGM] input: let down-arrow recall next line from history
2155 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2161 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2163 if (!TempBackwardActive) {
2164 TempBackwardActive = True;
2170 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2172 /* Check to see if triggered by a key release event for a repeating key.
2173 * If so the next queued event will be a key press of the same key at the same time */
2174 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2176 XPeekEvent(xDisplay, &next);
2177 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2178 next.xkey.keycode == event->xkey.keycode)
2182 TempBackwardActive = False;
2186 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2187 { // called as key binding
2190 if (nprms && *nprms > 0)
2194 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2200 { // called from menu
2201 ManInner(NULL, NULL, NULL, NULL);
2205 SetWindowTitle (char *text, char *title, char *icon)
2209 if (appData.titleInWindow) {
2211 XtSetArg(args[i], XtNlabel, text); i++;
2212 XtSetValues(titleWidget, args, i);
2215 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2216 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2217 XtSetValues(shellWidget, args, i);
2218 XSync(xDisplay, False);
2223 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2229 DisplayIcsInteractionTitle (String message)
2231 if (oldICSInteractionTitle == NULL) {
2232 /* Magic to find the old window title, adapted from vim */
2233 char *wina = getenv("WINDOWID");
2235 Window win = (Window) atoi(wina);
2236 Window root, parent, *children;
2237 unsigned int nchildren;
2238 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2240 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2241 if (!XQueryTree(xDisplay, win, &root, &parent,
2242 &children, &nchildren)) break;
2243 if (children) XFree((void *)children);
2244 if (parent == root || parent == 0) break;
2247 XSetErrorHandler(oldHandler);
2249 if (oldICSInteractionTitle == NULL) {
2250 oldICSInteractionTitle = "xterm";
2253 printf("\033]0;%s\007", message);
2258 XtIntervalId delayedEventTimerXID = 0;
2259 DelayedEventCallback delayedEventCallback = 0;
2264 delayedEventTimerXID = 0;
2265 delayedEventCallback();
2269 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2271 if(delayedEventTimerXID && delayedEventCallback == cb)
2272 // [HGM] alive: replace, rather than add or flush identical event
2273 XtRemoveTimeOut(delayedEventTimerXID);
2274 delayedEventCallback = cb;
2275 delayedEventTimerXID =
2276 XtAppAddTimeOut(appContext, millisec,
2277 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2280 DelayedEventCallback
2283 if (delayedEventTimerXID) {
2284 return delayedEventCallback;
2291 CancelDelayedEvent ()
2293 if (delayedEventTimerXID) {
2294 XtRemoveTimeOut(delayedEventTimerXID);
2295 delayedEventTimerXID = 0;
2299 XtIntervalId loadGameTimerXID = 0;
2302 LoadGameTimerRunning ()
2304 return loadGameTimerXID != 0;
2308 StopLoadGameTimer ()
2310 if (loadGameTimerXID != 0) {
2311 XtRemoveTimeOut(loadGameTimerXID);
2312 loadGameTimerXID = 0;
2320 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2322 loadGameTimerXID = 0;
2327 StartLoadGameTimer (long millisec)
2330 XtAppAddTimeOut(appContext, millisec,
2331 (XtTimerCallbackProc) LoadGameTimerCallback,
2335 XtIntervalId analysisClockXID = 0;
2338 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2340 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2341 || appData.icsEngineAnalyze) { // [DM]
2342 AnalysisPeriodicEvent(0);
2343 StartAnalysisClock();
2348 StartAnalysisClock ()
2351 XtAppAddTimeOut(appContext, 2000,
2352 (XtTimerCallbackProc) AnalysisClockCallback,
2356 XtIntervalId clockTimerXID = 0;
2359 ClockTimerRunning ()
2361 return clockTimerXID != 0;
2367 if (clockTimerXID != 0) {
2368 XtRemoveTimeOut(clockTimerXID);
2377 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2384 StartClockTimer (long millisec)
2387 XtAppAddTimeOut(appContext, millisec,
2388 (XtTimerCallbackProc) ClockTimerCallback,
2393 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2397 Widget w = (Widget) opt->handle;
2399 /* check for low time warning */
2400 Pixel foregroundOrWarningColor = timerForegroundPixel;
2403 appData.lowTimeWarning &&
2404 (timer / 1000) < appData.icsAlarmTime)
2405 foregroundOrWarningColor = lowTimeWarningColor;
2407 if (appData.clockMode) {
2408 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2409 XtSetArg(args[0], XtNlabel, buf);
2411 snprintf(buf, MSG_SIZ, "%s ", color);
2412 XtSetArg(args[0], XtNlabel, buf);
2417 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2418 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2420 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2421 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2424 XtSetValues(w, args, 3);
2427 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2430 SetClockIcon (int color)
2433 Pixmap pm = *clockIcons[color];
2434 if (iconPixmap != pm) {
2436 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2437 XtSetValues(shellWidget, args, 1);
2441 #define INPUT_SOURCE_BUF_SIZE 8192
2450 char buf[INPUT_SOURCE_BUF_SIZE];
2455 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2457 InputSource *is = (InputSource *) closure;
2462 if (is->lineByLine) {
2463 count = read(is->fd, is->unused,
2464 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2466 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2469 is->unused += count;
2471 while (p < is->unused) {
2472 q = memchr(p, '\n', is->unused - p);
2473 if (q == NULL) break;
2475 (is->func)(is, is->closure, p, q - p, 0);
2479 while (p < is->unused) {
2484 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2489 (is->func)(is, is->closure, is->buf, count, error);
2494 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2497 ChildProc *cp = (ChildProc *) pr;
2499 is = (InputSource *) calloc(1, sizeof(InputSource));
2500 is->lineByLine = lineByLine;
2504 is->fd = fileno(stdin);
2506 is->kind = cp->kind;
2507 is->fd = cp->fdFrom;
2510 is->unused = is->buf;
2513 is->xid = XtAppAddInput(appContext, is->fd,
2514 (XtPointer) (XtInputReadMask),
2515 (XtInputCallbackProc) DoInputCallback,
2517 is->closure = closure;
2518 return (InputSourceRef) is;
2522 RemoveInputSource (InputSourceRef isr)
2524 InputSource *is = (InputSource *) isr;
2526 if (is->xid == 0) return;
2527 XtRemoveInput(is->xid);
2533 static Boolean frameWaiting;
2536 FrameAlarm (int sig)
2538 frameWaiting = False;
2539 /* In case System-V style signals. Needed?? */
2540 signal(SIGALRM, FrameAlarm);
2544 FrameDelay (int time)
2546 struct itimerval delay;
2548 XSync(xDisplay, False);
2551 frameWaiting = True;
2552 signal(SIGALRM, FrameAlarm);
2553 delay.it_interval.tv_sec =
2554 delay.it_value.tv_sec = time / 1000;
2555 delay.it_interval.tv_usec =
2556 delay.it_value.tv_usec = (time % 1000) * 1000;
2557 setitimer(ITIMER_REAL, &delay, NULL);
2558 while (frameWaiting) pause();
2559 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2560 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2561 setitimer(ITIMER_REAL, &delay, NULL);
2568 FrameDelay (int time)
2570 XSync(xDisplay, False);
2572 usleep(time * 1000);
2578 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2580 char buf[MSG_SIZ], *logoName = buf;
2581 if(appData.logo[n][0]) {
2582 logoName = appData.logo[n];
2583 } else if(appData.autoLogo) {
2584 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2585 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2586 } else if(appData.directory[n] && appData.directory[n][0]) {
2587 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2591 { ASSIGN(cps->programLogo, logoName); }
2595 UpdateLogos (int displ)
2597 if(optList[W_WHITE-1].handle == NULL) return;
2598 LoadLogo(&first, 0, 0);
2599 LoadLogo(&second, 1, appData.icsActive);
2600 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);