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"
219 #define usleep(t) _sleep2(((t)+500)/1000)
223 # define _(s) gettext (s)
224 # define N_(s) gettext_noop (s)
230 int main P((int argc, char **argv));
231 RETSIGTYPE CmailSigHandler P((int sig));
232 RETSIGTYPE IntSigHandler P((int sig));
233 RETSIGTYPE TermSizeSigHandler P((int sig));
234 Widget CreateMenuBar P((Menu *mb, int boardWidth));
236 char *InsertPxlSize P((char *pattern, int targetPxlSize));
237 XFontSet CreateFontSet P((char *base_fnt_lst));
239 char *FindFont P((char *pattern, int targetPxlSize));
241 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
242 u_int wreq, u_int hreq));
243 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
244 void DelayedDrag P((void));
245 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
246 void HandlePV P((Widget w, XEvent * event,
247 String * params, Cardinal * nParams));
248 void DrawPositionProc P((Widget w, XEvent *event,
249 String *prms, Cardinal *nprms));
250 void CommentClick P((Widget w, XEvent * event,
251 String * params, Cardinal * nParams));
252 void ICSInputBoxPopUp P((void));
253 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
254 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
255 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
256 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
257 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
258 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
259 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
260 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
261 Boolean TempBackwardActive = False;
262 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
263 void DisplayMove P((int moveNumber));
264 void update_ics_width P(());
265 int CopyMemoProc P(());
268 * XBoard depends on Xt R4 or higher
270 int xtVersion = XtSpecificationRelease;
275 Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
276 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
277 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
278 Option *optList; // contains all widgets of main window
280 XFontSet fontSet, clockFontSet;
283 XFontStruct *clockFontStruct;
285 Font coordFontID, countFontID;
286 XFontStruct *coordFontStruct, *countFontStruct;
287 XtAppContext appContext;
290 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
292 Position commentX = -1, commentY = -1;
293 Dimension commentW, commentH;
294 typedef unsigned int BoardSize;
296 Boolean chessProgram;
298 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
299 int smallLayout = 0, tinyLayout = 0,
300 marginW, marginH, // [HGM] for run-time resizing
301 fromX = -1, fromY = -1, toX, toY, commentUp = False,
302 errorExitStatus = -1, defaultLineGap;
303 Dimension textHeight;
304 Pixel timerForegroundPixel, timerBackgroundPixel;
305 Pixel buttonForegroundPixel, buttonBackgroundPixel;
306 char *chessDir, *programName, *programVersion;
307 Boolean alwaysOnTop = False;
308 char *icsTextMenuString;
310 char *firstChessProgramNames;
311 char *secondChessProgramNames;
313 WindowPlacement wpMain;
314 WindowPlacement wpConsole;
315 WindowPlacement wpComment;
316 WindowPlacement wpMoveHistory;
317 WindowPlacement wpEvalGraph;
318 WindowPlacement wpEngineOutput;
319 WindowPlacement wpGameList;
320 WindowPlacement wpTags;
321 WindowPlacement wpDualBoard;
324 /* This magic number is the number of intermediate frames used
325 in each half of the animation. For short moves it's reduced
326 by 1. The total number of frames will be factor * 2 + 1. */
329 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
336 DropMenuEnables dmEnables[] = {
353 XtResource clientResources[] = {
354 { "flashCount", "flashCount", XtRInt, sizeof(int),
355 XtOffset(AppDataPtr, flashCount), XtRImmediate,
356 (XtPointer) FLASH_COUNT },
359 XrmOptionDescRec shellOptions[] = {
360 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
361 { "-flash", "flashCount", XrmoptionNoArg, "3" },
362 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
365 XtActionsRec boardActions[] = {
366 { "DrawPosition", DrawPositionProc },
367 { "HandlePV", HandlePV },
368 { "SelectPV", SelectPV },
369 { "StopPV", StopPV },
370 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
371 { "QuitProc", QuitWrapper },
372 { "ManProc", ManInner },
373 { "TempBackwardProc", TempBackwardProc },
374 { "TempForwardProc", TempForwardProc },
375 { "CommentClick", (XtActionProc) CommentClick },
376 { "GenericPopDown", (XtActionProc) GenericPopDown },
377 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
378 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
379 { "SelectMove", (XtActionProc) SelectMoveX },
380 { "LoadSelectedProc", LoadSelectedProc },
381 { "SetFilterProc", SetFilterProc },
382 { "TypeInProc", TypeInProc },
383 { "EnterKeyProc", EnterKeyProc },
384 { "UpKeyProc", UpKeyProc },
385 { "DownKeyProc", DownKeyProc },
386 { "WheelProc", WheelProc },
387 { "TabProc", TabProc },
390 char globalTranslations[] =
391 ":Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
392 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
393 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
394 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
395 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
396 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
397 :<Key>Pause: MenuItem(Mode.Pause) \n \
398 :Ctrl<Key>d: MenuItem(DebugProc) \n \
399 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
400 :<Key>Left: MenuItem(Edit.Backward) \n \
401 :<Key>Right: MenuItem(Edit.Forward) \n \
402 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
403 #ifndef OPTIONSDIALOG
405 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
406 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
407 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
408 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
409 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
412 :<KeyDown>Return: TempBackwardProc() \n \
413 :<KeyUp>Return: TempForwardProc() \n";
415 char ICSInputTranslations[] =
416 "<Key>Up: UpKeyProc() \n "
417 "<Key>Down: DownKeyProc() \n "
418 "<Key>Return: EnterKeyProc() \n";
420 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
421 // as the widget is destroyed before the up-click can call extend-end
422 char commentTranslations[] = "<Btn3Down>: extend-end(PRIMARY) select-start() CommentClick() \n";
424 String xboardResources[] = {
425 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
430 /* Max possible square size */
431 #define MAXSQSIZE 256
433 static int xpm_avail[MAXSQSIZE];
435 #ifdef HAVE_DIR_STRUCT
437 /* Extract piece size from filename */
439 xpm_getsize (char *name, int len, char *ext)
447 if ((p=strchr(name, '.')) == NULL ||
448 StrCaseCmp(p+1, ext) != 0)
454 while (*p && isdigit(*p))
461 /* Setup xpm_avail */
463 xpm_getavail (char *dirname, char *ext)
469 for (i=0; i<MAXSQSIZE; ++i)
472 if (appData.debugMode)
473 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
475 dir = opendir(dirname);
478 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
479 programName, dirname);
483 while ((ent=readdir(dir)) != NULL) {
484 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
485 if (i > 0 && i < MAXSQSIZE)
495 xpm_print_avail (FILE *fp, char *ext)
499 fprintf(fp, _("Available `%s' sizes:\n"), ext);
500 for (i=1; i<MAXSQSIZE; ++i) {
506 /* Return XPM piecesize closest to size */
508 xpm_closest_to (char *dirname, int size, char *ext)
511 int sm_diff = MAXSQSIZE;
515 xpm_getavail(dirname, ext);
517 if (appData.debugMode)
518 xpm_print_avail(stderr, ext);
520 for (i=1; i<MAXSQSIZE; ++i) {
523 diff = (diff<0) ? -diff : diff;
524 if (diff < sm_diff) {
532 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
538 #else /* !HAVE_DIR_STRUCT */
539 /* If we are on a system without a DIR struct, we can't
540 read the directory, so we can't collect a list of
541 filenames, etc., so we can't do any size-fitting. */
543 xpm_closest_to (char *dirname, int size, char *ext)
546 Warning: No DIR structure found on this system --\n\
547 Unable to autosize for XPM/XIM pieces.\n\
548 Please report this error to %s.\n\
549 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
552 #endif /* HAVE_DIR_STRUCT */
555 /* Arrange to catch delete-window events */
556 Atom wm_delete_window;
558 CatchDeleteWindow (Widget w, String procname)
561 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
562 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
563 XtAugmentTranslations(w, XtParseTranslationTable(buf));
570 XtSetArg(args[0], XtNiconic, False);
571 XtSetValues(shellWidget, args, 1);
573 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
576 //---------------------------------------------------------------------------------------------------------
577 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
580 #define CW_USEDEFAULT (1<<31)
581 #define ICS_TEXT_MENU_SIZE 90
582 #define DEBUG_FILE "xboard.debug"
583 #define SetCurrentDirectory chdir
584 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
588 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
591 // front-end part of option handling
593 // [HGM] This platform-dependent table provides the location for storing the color info
594 extern char *crWhite, * crBlack;
598 &appData.whitePieceColor,
599 &appData.blackPieceColor,
600 &appData.lightSquareColor,
601 &appData.darkSquareColor,
602 &appData.highlightSquareColor,
603 &appData.premoveHighlightColor,
604 &appData.lowTimeWarningColor,
615 // [HGM] font: keep a font for each square size, even non-stndard ones
618 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
619 char *fontTable[NUM_FONTS][MAX_SIZE];
622 ParseFont (char *name, int number)
623 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
625 if(sscanf(name, "size%d:", &size)) {
626 // [HGM] font: font is meant for specific boardSize (likely from settings file);
627 // defer processing it until we know if it matches our board size
628 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
629 fontTable[number][size] = strdup(strchr(name, ':')+1);
630 fontValid[number][size] = True;
635 case 0: // CLOCK_FONT
636 appData.clockFont = strdup(name);
638 case 1: // MESSAGE_FONT
639 appData.font = strdup(name);
641 case 2: // COORD_FONT
642 appData.coordFont = strdup(name);
647 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
652 { // only 2 fonts currently
653 appData.clockFont = CLOCK_FONT_NAME;
654 appData.coordFont = COORD_FONT_NAME;
655 appData.font = DEFAULT_FONT_NAME;
660 { // no-op, until we identify the code for this already in XBoard and move it here
664 ParseColor (int n, char *name)
665 { // in XBoard, just copy the color-name string
666 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
670 ParseTextAttribs (ColorClass cc, char *s)
672 (&appData.colorShout)[cc] = strdup(s);
676 ParseBoardSize (void *addr, char *name)
678 appData.boardSize = strdup(name);
683 { // In XBoard the sound-playing program takes care of obtaining the actual sound
687 SetCommPortDefaults ()
688 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
691 // [HGM] args: these three cases taken out to stay in front-end
693 SaveFontArg (FILE *f, ArgDescriptor *ad)
696 int i, n = (int)(intptr_t)ad->argLoc;
698 case 0: // CLOCK_FONT
699 name = appData.clockFont;
701 case 1: // MESSAGE_FONT
704 case 2: // COORD_FONT
705 name = appData.coordFont;
710 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
711 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
712 fontTable[n][squareSize] = strdup(name);
713 fontValid[n][squareSize] = True;
716 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
717 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
722 { // nothing to do, as the sounds are at all times represented by their text-string names already
726 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
727 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
728 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
732 SaveColor (FILE *f, ArgDescriptor *ad)
733 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
734 if(colorVariable[(int)(intptr_t)ad->argLoc])
735 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
739 SaveBoardSize (FILE *f, char *name, void *addr)
740 { // wrapper to shield back-end from BoardSize & sizeInfo
741 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
745 ParseCommPortSettings (char *s)
746 { // no such option in XBoard (yet)
752 GetActualPlacement (Widget wg, WindowPlacement *wp)
754 XWindowAttributes winAt;
761 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
762 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
763 wp->x = rx - winAt.x;
764 wp->y = ry - winAt.y;
765 wp->height = winAt.height;
766 wp->width = winAt.width;
767 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
772 { // wrapper to shield use of window handles from back-end (make addressible by number?)
773 // In XBoard this will have to wait until awareness of window parameters is implemented
774 GetActualPlacement(shellWidget, &wpMain);
775 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
776 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
777 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
778 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
779 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
780 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
784 PrintCommPortSettings (FILE *f, char *name)
785 { // This option does not exist in XBoard
789 EnsureOnScreen (int *x, int *y, int minX, int minY)
796 { // [HGM] args: allows testing if main window is realized from back-end
797 return xBoardWindow != 0;
801 PopUpStartupDialog ()
802 { // start menu not implemented in XBoard
806 ConvertToLine (int argc, char **argv)
808 static char line[128*1024], buf[1024];
812 for(i=1; i<argc; i++)
814 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
815 && argv[i][0] != '{' )
816 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
818 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
819 strncat(line, buf, 128*1024 - strlen(line) - 1 );
822 line[strlen(line)-1] = NULLCHAR;
826 //--------------------------------------------------------------------------------------------
829 ResizeBoardWindow (int w, int h, int inhibit)
831 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
833 shellArgs[0].value = w;
834 shellArgs[1].value = h;
835 shellArgs[4].value = shellArgs[2].value = w;
836 shellArgs[5].value = shellArgs[3].value = h;
837 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
839 XSync(xDisplay, False);
843 MakeOneColor (char *name, Pixel *color)
846 if (!appData.monoMode) {
847 vFrom.addr = (caddr_t) name;
848 vFrom.size = strlen(name);
849 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
850 if (vTo.addr == NULL) {
851 appData.monoMode = True;
854 *color = *(Pixel *) vTo.addr;
862 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
863 int forceMono = False;
865 if (appData.lowTimeWarning)
866 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
867 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
868 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
874 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
875 { // detervtomine what fonts to use, and create them
879 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
880 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
881 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
882 appData.font = fontTable[MESSAGE_FONT][squareSize];
883 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
884 appData.coordFont = fontTable[COORD_FONT][squareSize];
887 appData.font = InsertPxlSize(appData.font, fontPxlSize);
888 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
889 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
890 fontSet = CreateFontSet(appData.font);
891 clockFontSet = CreateFontSet(appData.clockFont);
893 /* For the coordFont, use the 0th font of the fontset. */
894 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
895 XFontStruct **font_struct_list;
896 XFontSetExtents *fontSize;
897 char **font_name_list;
898 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
899 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
900 coordFontStruct = XQueryFont(xDisplay, coordFontID);
901 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
902 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
905 appData.font = FindFont(appData.font, fontPxlSize);
906 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
907 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
908 clockFontID = XLoadFont(xDisplay, appData.clockFont);
909 clockFontStruct = XQueryFont(xDisplay, clockFontID);
910 coordFontID = XLoadFont(xDisplay, appData.coordFont);
911 coordFontStruct = XQueryFont(xDisplay, coordFontID);
912 // textHeight in !NLS mode!
914 countFontID = coordFontID; // [HGM] holdings
915 countFontStruct = coordFontStruct;
917 xdb = XtDatabase(xDisplay);
919 XrmPutLineResource(&xdb, "*international: True");
920 vTo.size = sizeof(XFontSet);
921 vTo.addr = (XtPointer) &fontSet;
922 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
924 XrmPutStringResource(&xdb, "*font", appData.font);
934 case ArgInt: p = " N"; break;
935 case ArgString: p = " STR"; break;
936 case ArgBoolean: p = " TF"; break;
937 case ArgSettingsFilename:
938 case ArgBackupSettingsFile:
939 case ArgFilename: p = " FILE"; break;
940 case ArgX: p = " Nx"; break;
941 case ArgY: p = " Ny"; break;
942 case ArgAttribs: p = " TEXTCOL"; break;
943 case ArgColor: p = " COL"; break;
944 case ArgFont: p = " FONT"; break;
945 case ArgBoardSize: p = " SIZE"; break;
946 case ArgFloat: p = " FLOAT"; break;
951 case ArgCommSettings:
958 GenerateGlobalTranslationTable (void)
960 /* go through all menu items and extract the keyboard shortcuts, so that X11 can load them */
968 /* loop over all menu entries */
969 for( i=0; menuBar[i].mi ; i++)
972 for(j=0; mi[j].proc; j++)
980 char *key,*test, *mods;
982 /* check for Ctrl/Alt */
983 if( strstr(mi[j].accel, "<Ctrl>") ) ctrl = 1;
984 if( strstr(mi[j].accel, "<Shift>") ) shift = 1;
985 if( strstr(mi[j].accel, "<Alt>") ) alt = 1;
987 /* remove all <...> */
988 test = strrchr(mi[j].accel, '>');
990 key = strdup(mi[j].accel);
992 key = strdup(++test); // remove ">"
994 /* instead of shift X11 uses the uppercase letter directly*/
995 if (shift && strlen(key)==1 )
997 *key = toupper(*key);
1001 /* handle some special cases which have different names in X11 */
1002 if ( strncmp(key, "Page_Down", 9) == 0 )
1007 else if ( strncmp(key, "Page_Up", 7) == 0 )
1010 key=strdup("Prior");
1013 /* create string of mods */
1015 mods = strdup("Ctrl ");
1021 mods = realloc(mods, strlen(mods) + strlen("Meta ")+1);
1022 strncat(mods, "Meta ", 5);
1027 mods = realloc(mods, strlen(mods) + strlen("Shift ")+1);
1028 strncat(mods, "Shift ", 6);
1031 // remove trailing space
1032 if( isspace(mods[strlen(mods)-1]) )
1033 mods[strlen(mods)-1]='\0';
1035 /* get the name for the callback, we can use MenuItem() here that will call KeyBindingProc */
1036 size_t namesize = snprintf(NULL, 0, "%s.%s", menuBar[i].ref, mi[j].ref);
1037 char *name = malloc(namesize+1);
1038 snprintf(name, namesize+1, "%s.%s", menuBar[i].ref, mi[j].ref);
1040 size_t buffersize = snprintf(NULL, 0, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
1041 char *buffer = malloc(buffersize+1);
1042 snprintf(buffer, buffersize+1, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
1044 /* add string to the output */
1045 output = realloc(output, strlen(output) + strlen(buffer)+1);
1046 strncat(output, buffer, strlen(buffer));
1065 ArgDescriptor *q, *p = argDescriptors+5;
1066 printf("\nXBoard accepts the following options:\n"
1067 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1068 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1069 " SIZE = board-size spec(s)\n"
1070 " Within parentheses are short forms, or options to set to true or false.\n"
1071 " Persistent options (saved in the settings file) are marked with *)\n\n");
1073 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1074 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1075 if(p->save) strcat(buf+len, "*");
1076 for(q=p+1; q->argLoc == p->argLoc; q++) {
1077 if(q->argName[0] == '-') continue;
1078 strcat(buf+len, q == p+1 ? " (" : " ");
1079 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1081 if(q != p+1) strcat(buf+len, ")");
1083 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1086 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1090 SlaveResize (Option *opt)
1095 main (int argc, char **argv)
1097 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1098 XSetWindowAttributes window_attributes;
1100 Dimension boardWidth, boardHeight, w, h;
1102 int forceMono = False;
1104 srandom(time(0)); // [HGM] book: make random truly random
1106 setbuf(stdout, NULL);
1107 setbuf(stderr, NULL);
1110 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1111 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1115 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1120 programName = strrchr(argv[0], '/');
1121 if (programName == NULL)
1122 programName = argv[0];
1127 XtSetLanguageProc(NULL, NULL, NULL);
1128 if (appData.debugMode) {
1129 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1132 bindtextdomain(PACKAGE, LOCALEDIR);
1133 textdomain(PACKAGE);
1136 appData.boardSize = "";
1137 InitAppData(ConvertToLine(argc, argv));
1139 if (p == NULL) p = "/tmp";
1140 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1141 gameCopyFilename = (char*) malloc(i);
1142 gamePasteFilename = (char*) malloc(i);
1143 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1144 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1146 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1147 static char buf[MSG_SIZ];
1148 EscapeExpand(buf, appData.firstInitString);
1149 appData.firstInitString = strdup(buf);
1150 EscapeExpand(buf, appData.secondInitString);
1151 appData.secondInitString = strdup(buf);
1152 EscapeExpand(buf, appData.firstComputerString);
1153 appData.firstComputerString = strdup(buf);
1154 EscapeExpand(buf, appData.secondComputerString);
1155 appData.secondComputerString = strdup(buf);
1158 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1161 if (chdir(chessDir) != 0) {
1162 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1168 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1169 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1170 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1171 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1174 setbuf(debugFP, NULL);
1177 /* [HGM,HR] make sure board size is acceptable */
1178 if(appData.NrFiles > BOARD_FILES ||
1179 appData.NrRanks > BOARD_RANKS )
1180 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1183 /* This feature does not work; animation needs a rewrite */
1184 appData.highlightDragging = FALSE;
1188 gameInfo.variant = StringToVariant(appData.variant);
1189 InitPosition(FALSE);
1192 XtAppInitialize(&appContext, "XBoard", shellOptions,
1193 XtNumber(shellOptions),
1194 &argc, argv, xboardResources, NULL, 0);
1196 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1197 clientResources, XtNumber(clientResources),
1200 xDisplay = XtDisplay(shellWidget);
1201 xScreen = DefaultScreen(xDisplay);
1202 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1205 * determine size, based on supplied or remembered -size, or screen size
1207 if (isdigit(appData.boardSize[0])) {
1208 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1209 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1210 &fontPxlSize, &smallLayout, &tinyLayout);
1212 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1213 programName, appData.boardSize);
1217 /* Find some defaults; use the nearest known size */
1218 SizeDefaults *szd, *nearest;
1219 int distance = 99999;
1220 nearest = szd = sizeDefaults;
1221 while (szd->name != NULL) {
1222 if (abs(szd->squareSize - squareSize) < distance) {
1224 distance = abs(szd->squareSize - squareSize);
1225 if (distance == 0) break;
1229 if (i < 2) lineGap = nearest->lineGap;
1230 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1231 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1232 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1233 if (i < 6) smallLayout = nearest->smallLayout;
1234 if (i < 7) tinyLayout = nearest->tinyLayout;
1237 SizeDefaults *szd = sizeDefaults;
1238 if (*appData.boardSize == NULLCHAR) {
1239 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1240 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1243 if (szd->name == NULL) szd--;
1244 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1246 while (szd->name != NULL &&
1247 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1248 if (szd->name == NULL) {
1249 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1250 programName, appData.boardSize);
1254 squareSize = szd->squareSize;
1255 lineGap = szd->lineGap;
1256 clockFontPxlSize = szd->clockFontPxlSize;
1257 coordFontPxlSize = szd->coordFontPxlSize;
1258 fontPxlSize = szd->fontPxlSize;
1259 smallLayout = szd->smallLayout;
1260 tinyLayout = szd->tinyLayout;
1261 // [HGM] font: use defaults from settings file if available and not overruled
1264 defaultLineGap = lineGap;
1265 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1267 /* [HR] height treated separately (hacked) */
1268 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1269 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1272 * Determine what fonts to use.
1274 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1277 * Detect if there are not enough colors available and adapt.
1279 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1280 appData.monoMode = True;
1283 forceMono = MakeColors();
1286 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1288 appData.monoMode = True;
1291 if (appData.monoMode && appData.debugMode) {
1292 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1293 (unsigned long) XWhitePixel(xDisplay, xScreen),
1294 (unsigned long) XBlackPixel(xDisplay, xScreen));
1297 ParseIcsTextColors();
1299 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1305 layoutName = "tinyLayout";
1306 } else if (smallLayout) {
1307 layoutName = "smallLayout";
1309 layoutName = "normalLayout";
1312 optList = BoardPopUp(squareSize, lineGap, (void*)
1318 InitDrawingHandle(optList + W_BOARD);
1319 currBoard = &optList[W_BOARD];
1320 boardWidget = optList[W_BOARD].handle;
1321 menuBarWidget = optList[W_MENU].handle;
1322 dropMenu = optList[W_DROP].handle;
1323 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1324 formWidget = XtParent(boardWidget);
1325 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1326 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1327 XtGetValues(optList[W_WHITE].handle, args, 2);
1328 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1329 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1330 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1331 XtGetValues(optList[W_PAUSE].handle, args, 2);
1334 xBoardWindow = XtWindow(boardWidget);
1336 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1337 // not need to go into InitDrawingSizes().
1340 * Create X checkmark bitmap and initialize option menu checks.
1342 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1343 checkmark_bits, checkmark_width, checkmark_height);
1349 ReadBitmap(&wIconPixmap, "icon_white.bm",
1350 icon_white_bits, icon_white_width, icon_white_height);
1351 ReadBitmap(&bIconPixmap, "icon_black.bm",
1352 icon_black_bits, icon_black_width, icon_black_height);
1353 iconPixmap = wIconPixmap;
1355 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1356 XtSetValues(shellWidget, args, i);
1359 * Create a cursor for the board widget.
1361 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1362 XChangeWindowAttributes(xDisplay, xBoardWindow,
1363 CWCursor, &window_attributes);
1366 * Inhibit shell resizing.
1368 shellArgs[0].value = (XtArgVal) &w;
1369 shellArgs[1].value = (XtArgVal) &h;
1370 XtGetValues(shellWidget, shellArgs, 2);
1371 shellArgs[4].value = shellArgs[2].value = w;
1372 shellArgs[5].value = shellArgs[3].value = h;
1373 // XtSetValues(shellWidget, &shellArgs[2], 4);
1374 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1375 marginH = h - boardHeight;
1377 CatchDeleteWindow(shellWidget, "QuitProc");
1382 if(appData.logoSize)
1383 { // locate and read user logo
1385 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1386 ASSIGN(userLogo, buf);
1389 if (appData.animate || appData.animateDragging)
1393 char *TranslationsTableMenus=GenerateGlobalTranslationTable ();
1395 XtAugmentTranslations(formWidget,
1396 XtParseTranslationTable(globalTranslations));
1397 XtAugmentTranslations(formWidget,
1398 XtParseTranslationTable(TranslationsTableMenus));
1400 XtAddEventHandler(formWidget, KeyPressMask, False,
1401 (XtEventHandler) MoveTypeInProc, NULL);
1402 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1403 (XtEventHandler) EventProc, NULL);
1405 /* [AS] Restore layout */
1406 if( wpMoveHistory.visible ) {
1410 if( wpEvalGraph.visible )
1415 if( wpEngineOutput.visible ) {
1416 EngineOutputPopUp();
1421 if (errorExitStatus == -1) {
1422 if (appData.icsActive) {
1423 /* We now wait until we see "login:" from the ICS before
1424 sending the logon script (problems with timestamp otherwise) */
1425 /*ICSInitScript();*/
1426 if (appData.icsInputBox) ICSInputBoxPopUp();
1430 signal(SIGWINCH, TermSizeSigHandler);
1432 signal(SIGINT, IntSigHandler);
1433 signal(SIGTERM, IntSigHandler);
1434 if (*appData.cmailGameName != NULLCHAR) {
1435 signal(SIGUSR1, CmailSigHandler);
1439 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1442 // XtSetKeyboardFocus(shellWidget, formWidget);
1443 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1445 XtAppMainLoop(appContext);
1446 if (appData.debugMode) fclose(debugFP); // [DM] debug
1451 TermSizeSigHandler (int sig)
1457 IntSigHandler (int sig)
1463 CmailSigHandler (int sig)
1468 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1470 /* Activate call-back function CmailSigHandlerCallBack() */
1471 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1473 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1477 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1480 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1482 /**** end signal code ****/
1485 #define Abs(n) ((n)<0 ? -(n) : (n))
1489 InsertPxlSize (char *pattern, int targetPxlSize)
1491 char *base_fnt_lst, strInt[12], *p, *q;
1492 int alternatives, i, len, strIntLen;
1495 * Replace the "*" (if present) in the pixel-size slot of each
1496 * alternative with the targetPxlSize.
1500 while ((p = strchr(p, ',')) != NULL) {
1504 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1505 strIntLen = strlen(strInt);
1506 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1510 while (alternatives--) {
1511 char *comma = strchr(p, ',');
1512 for (i=0; i<14; i++) {
1513 char *hyphen = strchr(p, '-');
1515 if (comma && hyphen > comma) break;
1516 len = hyphen + 1 - p;
1517 if (i == 7 && *p == '*' && len == 2) {
1519 memcpy(q, strInt, strIntLen);
1529 len = comma + 1 - p;
1536 return base_fnt_lst;
1540 CreateFontSet (char *base_fnt_lst)
1543 char **missing_list;
1547 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1548 &missing_list, &missing_count, &def_string);
1549 if (appData.debugMode) {
1551 XFontStruct **font_struct_list;
1552 char **font_name_list;
1553 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1555 fprintf(debugFP, " got list %s, locale %s\n",
1556 XBaseFontNameListOfFontSet(fntSet),
1557 XLocaleOfFontSet(fntSet));
1558 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1559 for (i = 0; i < count; i++) {
1560 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1563 for (i = 0; i < missing_count; i++) {
1564 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1567 if (fntSet == NULL) {
1568 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1573 #else // not ENABLE_NLS
1575 * Find a font that matches "pattern" that is as close as
1576 * possible to the targetPxlSize. Prefer fonts that are k
1577 * pixels smaller to fonts that are k pixels larger. The
1578 * pattern must be in the X Consortium standard format,
1579 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1580 * The return value should be freed with XtFree when no
1584 FindFont (char *pattern, int targetPxlSize)
1586 char **fonts, *p, *best, *scalable, *scalableTail;
1587 int i, j, nfonts, minerr, err, pxlSize;
1589 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1591 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1592 programName, pattern);
1599 for (i=0; i<nfonts; i++) {
1602 if (*p != '-') continue;
1604 if (*p == NULLCHAR) break;
1605 if (*p++ == '-') j++;
1607 if (j < 7) continue;
1610 scalable = fonts[i];
1613 err = pxlSize - targetPxlSize;
1614 if (Abs(err) < Abs(minerr) ||
1615 (minerr > 0 && err < 0 && -err == minerr)) {
1621 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1622 /* If the error is too big and there is a scalable font,
1623 use the scalable font. */
1624 int headlen = scalableTail - scalable;
1625 p = (char *) XtMalloc(strlen(scalable) + 10);
1626 while (isdigit(*scalableTail)) scalableTail++;
1627 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1629 p = (char *) XtMalloc(strlen(best) + 2);
1630 safeStrCpy(p, best, strlen(best)+1 );
1632 if (appData.debugMode) {
1633 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1634 pattern, targetPxlSize, p);
1636 XFreeFontNames(fonts);
1642 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1645 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1651 MarkMenuItem (char *menuRef, int state)
1653 MenuItem *item = MenuNameToItem(menuRef);
1657 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1658 XtSetValues(item->handle, args, 1);
1663 EnableNamedMenuItem (char *menuRef, int state)
1665 MenuItem *item = MenuNameToItem(menuRef);
1667 if(item) XtSetSensitive(item->handle, state);
1671 EnableButtonBar (int state)
1673 XtSetSensitive(optList[W_BUTTON].handle, state);
1678 SetMenuEnables (Enables *enab)
1680 while (enab->name != NULL) {
1681 EnableNamedMenuItem(enab->name, enab->value);
1687 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1688 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1690 if(*nprms == 0) return;
1691 item = MenuNameToItem(prms[0]);
1692 if(item) ((MenuProc *) item->proc) ();
1704 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1705 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1706 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1707 dmEnables[i].piece);
1708 XtSetSensitive(entry, p != NULL || !appData.testLegality
1709 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1710 && !appData.icsActive));
1712 while (p && *p++ == dmEnables[i].piece) count++;
1713 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1715 XtSetArg(args[j], XtNlabel, label); j++;
1716 XtSetValues(entry, args, j);
1721 do_flash_delay (unsigned long msec)
1727 FlashDelay (int flash_delay)
1729 XSync(xDisplay, False);
1730 if(flash_delay) do_flash_delay(flash_delay);
1734 Fraction (int x, int start, int stop)
1736 double f = ((double) x - start)/(stop - start);
1737 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1741 static WindowPlacement wpNew;
1744 CoDrag (Widget sh, WindowPlacement *wp)
1747 int j=0, touch=0, fudge = 2;
1748 GetActualPlacement(sh, wp);
1749 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1750 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1751 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1752 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1753 if(!touch ) return; // only windows that touch co-move
1754 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1755 int heightInc = wpNew.height - wpMain.height;
1756 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1757 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1758 wp->y += fracTop * heightInc;
1759 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1760 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1761 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1762 int widthInc = wpNew.width - wpMain.width;
1763 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1764 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1765 wp->y += fracLeft * widthInc;
1766 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1767 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1769 wp->x += wpNew.x - wpMain.x;
1770 wp->y += wpNew.y - wpMain.y;
1771 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1772 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1773 XtSetArg(args[j], XtNx, wp->x); j++;
1774 XtSetArg(args[j], XtNy, wp->y); j++;
1775 XtSetValues(sh, args, j);
1779 ReSize (WindowPlacement *wp)
1782 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1783 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1784 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1785 if(sqy < sqx) sqx = sqy;
1786 if(sqx != squareSize) {
1787 squareSize = sqx; // adopt new square size
1788 CreatePNGPieces(); // make newly scaled pieces
1789 InitDrawingSizes(0, 0); // creates grid etc.
1790 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1791 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1792 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1793 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1794 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1797 static XtIntervalId delayedDragID = 0;
1806 GetActualPlacement(shellWidget, &wpNew);
1807 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1808 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1809 busy = 0; return; // false alarm
1812 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1813 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1814 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1815 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1817 DrawPosition(True, NULL);
1818 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1826 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1828 XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1832 EventProc (Widget widget, caddr_t unused, XEvent *event)
1834 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1835 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1839 * event handler for redrawing the board
1842 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1844 DrawPosition(True, NULL);
1849 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1850 { // [HGM] pv: walk PV
1851 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1854 extern int savedIndex; /* gross that this is global */
1857 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1860 XawTextPosition index, dummy;
1863 XawTextGetSelectionPos(w, &index, &dummy);
1864 XtSetArg(arg, XtNstring, &val);
1865 XtGetValues(w, &arg, 1);
1866 ReplaceComment(savedIndex, val);
1867 if(savedIndex != currentMove) ToNrEvent(savedIndex);
1868 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1872 /* Disable all user input other than deleting the window */
1873 static int frozen = 0;
1879 /* Grab by a widget that doesn't accept input */
1880 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1884 /* Undo a FreezeUI */
1888 if (!frozen) return;
1889 XtRemoveGrab(optList[W_MESSG].handle);
1897 static int oldPausing = FALSE;
1898 static GameMode oldmode = (GameMode) -1;
1901 if (!boardWidget || !XtIsRealized(boardWidget)) return;
1903 if (pausing != oldPausing) {
1904 oldPausing = pausing;
1905 MarkMenuItem("Mode.Pause", pausing);
1907 if (appData.showButtonBar) {
1908 /* Always toggle, don't set. Previous code messes up when
1909 invoked while the button is pressed, as releasing it
1910 toggles the state again. */
1913 XtSetArg(args[0], XtNbackground, &oldbg);
1914 XtSetArg(args[1], XtNforeground, &oldfg);
1915 XtGetValues(optList[W_PAUSE].handle,
1917 XtSetArg(args[0], XtNbackground, oldfg);
1918 XtSetArg(args[1], XtNforeground, oldbg);
1920 XtSetValues(optList[W_PAUSE].handle, args, 2);
1924 wname = ModeToWidgetName(oldmode);
1925 if (wname != NULL) {
1926 MarkMenuItem(wname, False);
1928 wname = ModeToWidgetName(gameMode);
1929 if (wname != NULL) {
1930 MarkMenuItem(wname, True);
1933 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1935 /* Maybe all the enables should be handled here, not just this one */
1936 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1938 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1943 * Button/menu procedures
1946 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1947 char *selected_fen_position=NULL;
1950 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1951 Atom *type_return, XtPointer *value_return,
1952 unsigned long *length_return, int *format_return)
1954 char *selection_tmp;
1956 // if (!selected_fen_position) return False; /* should never happen */
1957 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1958 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1959 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1962 if (f == NULL) return False;
1966 selection_tmp = XtMalloc(len + 1);
1967 count = fread(selection_tmp, 1, len, f);
1970 XtFree(selection_tmp);
1973 selection_tmp[len] = NULLCHAR;
1975 /* note: since no XtSelectionDoneProc was registered, Xt will
1976 * automatically call XtFree on the value returned. So have to
1977 * make a copy of it allocated with XtMalloc */
1978 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1979 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1982 *value_return=selection_tmp;
1983 *length_return=strlen(selection_tmp);
1984 *type_return=*target;
1985 *format_return = 8; /* bits per byte */
1987 } else if (*target == XA_TARGETS(xDisplay)) {
1988 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1989 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1990 targets_tmp[1] = XA_STRING;
1991 *value_return = targets_tmp;
1992 *type_return = XA_ATOM;
1995 // This code leads to a read of value_return out of bounds on 64-bit systems.
1996 // Other code which I have seen always sets *format_return to 32 independent of
1997 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1998 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1999 *format_return = 8 * sizeof(Atom);
2000 if (*format_return > 32) {
2001 *length_return *= *format_return / 32;
2002 *format_return = 32;
2005 *format_return = 32;
2013 /* note: when called from menu all parameters are NULL, so no clue what the
2014 * Widget which was clicked on was, or what the click event was
2017 CopySomething (char *src)
2019 selected_fen_position = src;
2021 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2022 * have a notion of a position that is selected but not copied.
2023 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2025 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2027 SendPositionSelection,
2028 NULL/* lose_ownership_proc */ ,
2029 NULL/* transfer_done_proc */);
2030 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2032 SendPositionSelection,
2033 NULL/* lose_ownership_proc */ ,
2034 NULL/* transfer_done_proc */);
2037 /* function called when the data to Paste is ready */
2039 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2040 Atom *type, XtPointer value, unsigned long *len, int *format)
2043 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2044 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2045 EditPositionPasteFEN(fenstr);
2049 /* called when Paste Position button is pressed,
2050 * all parameters will be NULL */
2052 PastePositionProc ()
2054 XtGetSelectionValue(menuBarWidget,
2055 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2056 /* (XtSelectionCallbackProc) */ PastePositionCB,
2057 NULL, /* client_data passed to PastePositionCB */
2059 /* better to use the time field from the event that triggered the
2060 * call to this function, but that isn't trivial to get
2067 /* note: when called from menu all parameters are NULL, so no clue what the
2068 * Widget which was clicked on was, or what the click event was
2070 /* function called when the data to Paste is ready */
2072 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2073 Atom *type, XtPointer value, unsigned long *len, int *format)
2076 if (value == NULL || *len == 0) {
2077 return; /* nothing had been selected to copy */
2079 f = fopen(gamePasteFilename, "w");
2081 DisplayError(_("Can't open temp file"), errno);
2084 fwrite(value, 1, *len, f);
2087 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2090 /* called when Paste Game button is pressed,
2091 * all parameters will be NULL */
2095 XtGetSelectionValue(menuBarWidget,
2096 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2097 /* (XtSelectionCallbackProc) */ PasteGameCB,
2098 NULL, /* client_data passed to PasteGameCB */
2100 /* better to use the time field from the event that triggered the
2101 * call to this function, but that isn't trivial to get
2110 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2117 { // bassic primitive for determining if modifier keys are pressed
2118 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2121 XQueryKeymap(xDisplay,keys);
2122 for(i=0; i<6; i++) {
2124 j = XKeysymToKeycode(xDisplay, codes[i]);
2125 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2131 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2135 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2136 if ( n == 1 && *buf >= 32 // printable
2137 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2138 ) BoxAutoPopUp (buf);
2142 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2143 { // [HGM] input: let up-arrow recall previous line from history
2148 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2149 { // [HGM] input: let down-arrow recall next line from history
2154 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2160 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2162 if (!TempBackwardActive) {
2163 TempBackwardActive = True;
2169 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2171 /* Check to see if triggered by a key release event for a repeating key.
2172 * If so the next queued event will be a key press of the same key at the same time */
2173 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2175 XPeekEvent(xDisplay, &next);
2176 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2177 next.xkey.keycode == event->xkey.keycode)
2181 TempBackwardActive = False;
2185 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2186 { // called as key binding
2189 if (nprms && *nprms > 0)
2193 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2199 { // called from menu
2200 ManInner(NULL, NULL, NULL, NULL);
2204 SetWindowTitle (char *text, char *title, char *icon)
2208 if (appData.titleInWindow) {
2210 XtSetArg(args[i], XtNlabel, text); i++;
2211 XtSetValues(titleWidget, args, i);
2214 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2215 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2216 XtSetValues(shellWidget, args, i);
2217 XSync(xDisplay, False);
2222 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2228 DisplayIcsInteractionTitle (String message)
2230 if (oldICSInteractionTitle == NULL) {
2231 /* Magic to find the old window title, adapted from vim */
2232 char *wina = getenv("WINDOWID");
2234 Window win = (Window) atoi(wina);
2235 Window root, parent, *children;
2236 unsigned int nchildren;
2237 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2239 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2240 if (!XQueryTree(xDisplay, win, &root, &parent,
2241 &children, &nchildren)) break;
2242 if (children) XFree((void *)children);
2243 if (parent == root || parent == 0) break;
2246 XSetErrorHandler(oldHandler);
2248 if (oldICSInteractionTitle == NULL) {
2249 oldICSInteractionTitle = "xterm";
2252 printf("\033]0;%s\007", message);
2257 XtIntervalId delayedEventTimerXID = 0;
2258 DelayedEventCallback delayedEventCallback = 0;
2263 delayedEventTimerXID = 0;
2264 delayedEventCallback();
2268 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2270 if(delayedEventTimerXID && delayedEventCallback == cb)
2271 // [HGM] alive: replace, rather than add or flush identical event
2272 XtRemoveTimeOut(delayedEventTimerXID);
2273 delayedEventCallback = cb;
2274 delayedEventTimerXID =
2275 XtAppAddTimeOut(appContext, millisec,
2276 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2279 DelayedEventCallback
2282 if (delayedEventTimerXID) {
2283 return delayedEventCallback;
2290 CancelDelayedEvent ()
2292 if (delayedEventTimerXID) {
2293 XtRemoveTimeOut(delayedEventTimerXID);
2294 delayedEventTimerXID = 0;
2298 XtIntervalId loadGameTimerXID = 0;
2301 LoadGameTimerRunning ()
2303 return loadGameTimerXID != 0;
2307 StopLoadGameTimer ()
2309 if (loadGameTimerXID != 0) {
2310 XtRemoveTimeOut(loadGameTimerXID);
2311 loadGameTimerXID = 0;
2319 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2321 loadGameTimerXID = 0;
2326 StartLoadGameTimer (long millisec)
2329 XtAppAddTimeOut(appContext, millisec,
2330 (XtTimerCallbackProc) LoadGameTimerCallback,
2334 XtIntervalId analysisClockXID = 0;
2337 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2339 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2340 || appData.icsEngineAnalyze) { // [DM]
2341 AnalysisPeriodicEvent(0);
2342 StartAnalysisClock();
2347 StartAnalysisClock ()
2350 XtAppAddTimeOut(appContext, 2000,
2351 (XtTimerCallbackProc) AnalysisClockCallback,
2355 XtIntervalId clockTimerXID = 0;
2358 ClockTimerRunning ()
2360 return clockTimerXID != 0;
2366 if (clockTimerXID != 0) {
2367 XtRemoveTimeOut(clockTimerXID);
2376 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2383 StartClockTimer (long millisec)
2386 XtAppAddTimeOut(appContext, millisec,
2387 (XtTimerCallbackProc) ClockTimerCallback,
2392 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2396 Widget w = (Widget) opt->handle;
2398 /* check for low time warning */
2399 Pixel foregroundOrWarningColor = timerForegroundPixel;
2402 appData.lowTimeWarning &&
2403 (timer / 1000) < appData.icsAlarmTime)
2404 foregroundOrWarningColor = lowTimeWarningColor;
2406 if (appData.clockMode) {
2407 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2408 XtSetArg(args[0], XtNlabel, buf);
2410 snprintf(buf, MSG_SIZ, "%s ", color);
2411 XtSetArg(args[0], XtNlabel, buf);
2416 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2417 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2419 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2420 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2423 XtSetValues(w, args, 3);
2426 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2429 SetClockIcon (int color)
2432 Pixmap pm = *clockIcons[color];
2433 if (iconPixmap != pm) {
2435 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2436 XtSetValues(shellWidget, args, 1);
2440 #define INPUT_SOURCE_BUF_SIZE 8192
2449 char buf[INPUT_SOURCE_BUF_SIZE];
2454 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2456 InputSource *is = (InputSource *) closure;
2461 if (is->lineByLine) {
2462 count = read(is->fd, is->unused,
2463 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2465 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2468 is->unused += count;
2470 while (p < is->unused) {
2471 q = memchr(p, '\n', is->unused - p);
2472 if (q == NULL) break;
2474 (is->func)(is, is->closure, p, q - p, 0);
2478 while (p < is->unused) {
2483 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2488 (is->func)(is, is->closure, is->buf, count, error);
2493 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2496 ChildProc *cp = (ChildProc *) pr;
2498 is = (InputSource *) calloc(1, sizeof(InputSource));
2499 is->lineByLine = lineByLine;
2503 is->fd = fileno(stdin);
2505 is->kind = cp->kind;
2506 is->fd = cp->fdFrom;
2509 is->unused = is->buf;
2512 is->xid = XtAppAddInput(appContext, is->fd,
2513 (XtPointer) (XtInputReadMask),
2514 (XtInputCallbackProc) DoInputCallback,
2516 is->closure = closure;
2517 return (InputSourceRef) is;
2521 RemoveInputSource (InputSourceRef isr)
2523 InputSource *is = (InputSource *) isr;
2525 if (is->xid == 0) return;
2526 XtRemoveInput(is->xid);
2532 static Boolean frameWaiting;
2535 FrameAlarm (int sig)
2537 frameWaiting = False;
2538 /* In case System-V style signals. Needed?? */
2539 signal(SIGALRM, FrameAlarm);
2543 FrameDelay (int time)
2545 struct itimerval delay;
2547 XSync(xDisplay, False);
2550 frameWaiting = True;
2551 signal(SIGALRM, FrameAlarm);
2552 delay.it_interval.tv_sec =
2553 delay.it_value.tv_sec = time / 1000;
2554 delay.it_interval.tv_usec =
2555 delay.it_value.tv_usec = (time % 1000) * 1000;
2556 setitimer(ITIMER_REAL, &delay, NULL);
2557 while (frameWaiting) pause();
2558 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2559 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2560 setitimer(ITIMER_REAL, &delay, NULL);
2567 FrameDelay (int time)
2569 XSync(xDisplay, False);
2571 usleep(time * 1000);
2577 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2579 char buf[MSG_SIZ], *logoName = buf;
2580 if(appData.logo[n][0]) {
2581 logoName = appData.logo[n];
2582 } else if(appData.autoLogo) {
2583 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2584 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2585 } else if(appData.directory[n] && appData.directory[n][0]) {
2586 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2590 { ASSIGN(cps->programLogo, logoName); }
2594 UpdateLogos (int displ)
2596 if(optList[W_WHITE-1].handle == NULL) return;
2597 LoadLogo(&first, 0, 0);
2598 LoadLogo(&second, 1, appData.icsActive);
2599 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);