2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
64 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.h>
68 # if HAVE_SYS_SOCKET_H
69 # include <sys/socket.h>
70 # include <netinet/in.h>
72 # else /* not HAVE_SYS_SOCKET_H */
73 # if HAVE_LAN_SOCKET_H
74 # include <lan/socket.h>
76 # include <lan/netdb.h>
77 # else /* not HAVE_LAN_SOCKET_H */
78 # define OMIT_SOCKETS 1
79 # endif /* not HAVE_LAN_SOCKET_H */
80 # endif /* not HAVE_SYS_SOCKET_H */
81 #endif /* !OMIT_SOCKETS */
86 #else /* not STDC_HEADERS */
87 extern char *getenv();
90 # else /* not HAVE_STRING_H */
92 # endif /* not HAVE_STRING_H */
93 #endif /* not STDC_HEADERS */
96 # include <sys/fcntl.h>
97 #else /* not HAVE_SYS_FCNTL_H */
100 # endif /* HAVE_FCNTL_H */
101 #endif /* not HAVE_SYS_FCNTL_H */
103 #if HAVE_SYS_SYSTEMINFO_H
104 # include <sys/systeminfo.h>
105 #endif /* HAVE_SYS_SYSTEMINFO_H */
107 #if TIME_WITH_SYS_TIME
108 # include <sys/time.h>
112 # include <sys/time.h>
123 # include <sys/wait.h>
128 # define NAMLEN(dirent) strlen((dirent)->d_name)
129 # define HAVE_DIR_STRUCT
131 # define dirent direct
132 # define NAMLEN(dirent) (dirent)->d_namlen
134 # include <sys/ndir.h>
135 # define HAVE_DIR_STRUCT
138 # include <sys/dir.h>
139 # define HAVE_DIR_STRUCT
143 # define HAVE_DIR_STRUCT
151 #include <X11/Intrinsic.h>
152 #include <X11/StringDefs.h>
153 #include <X11/Shell.h>
154 #include <X11/cursorfont.h>
155 #include <X11/Xatom.h>
156 #include <X11/Xmu/Atoms.h>
158 #include <X11/Xaw3d/Dialog.h>
159 #include <X11/Xaw3d/Form.h>
160 #include <X11/Xaw3d/List.h>
161 #include <X11/Xaw3d/Label.h>
162 #include <X11/Xaw3d/SimpleMenu.h>
163 #include <X11/Xaw3d/SmeBSB.h>
164 #include <X11/Xaw3d/SmeLine.h>
165 #include <X11/Xaw3d/Box.h>
166 #include <X11/Xaw3d/MenuButton.h>
167 #include <X11/Xaw3d/Text.h>
168 #include <X11/Xaw3d/AsciiText.h>
170 #include <X11/Xaw/Dialog.h>
171 #include <X11/Xaw/Form.h>
172 #include <X11/Xaw/List.h>
173 #include <X11/Xaw/Label.h>
174 #include <X11/Xaw/SimpleMenu.h>
175 #include <X11/Xaw/SmeBSB.h>
176 #include <X11/Xaw/SmeLine.h>
177 #include <X11/Xaw/Box.h>
178 #include <X11/Xaw/MenuButton.h>
179 #include <X11/Xaw/Text.h>
180 #include <X11/Xaw/AsciiText.h>
183 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
188 #define IMAGE_EXT "xpm"
190 #define IMAGE_EXT "xim"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
208 #include "engineoutput.h"
218 #define usleep(t) _sleep2(((t)+500)/1000)
222 # define _(s) gettext (s)
223 # define N_(s) gettext_noop (s)
229 int main P((int argc, char **argv));
230 RETSIGTYPE CmailSigHandler P((int sig));
231 RETSIGTYPE IntSigHandler P((int sig));
232 RETSIGTYPE TermSizeSigHandler P((int sig));
233 Widget CreateMenuBar P((Menu *mb, int boardWidth));
235 char *InsertPxlSize P((char *pattern, int targetPxlSize));
236 XFontSet CreateFontSet P((char *base_fnt_lst));
238 char *FindFont P((char *pattern, int targetPxlSize));
240 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
241 u_int wreq, u_int hreq));
242 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
243 void DelayedDrag P((void));
244 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
245 void HandlePV P((Widget w, XEvent * event,
246 String * params, Cardinal * nParams));
247 void DrawPositionProc P((Widget w, XEvent *event,
248 String *prms, Cardinal *nprms));
249 void CommentClick P((Widget w, XEvent * event,
250 String * params, Cardinal * nParams));
251 void ICSInputBoxPopUp P((void));
252 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
253 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
254 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
255 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
256 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
257 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
258 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
259 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
260 Boolean TempBackwardActive = False;
261 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
262 void DisplayMove P((int moveNumber));
263 void update_ics_width P(());
264 int CopyMemoProc P(());
267 * XBoard depends on Xt R4 or higher
269 int xtVersion = XtSpecificationRelease;
274 Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
275 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
276 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
277 Option *optList; // contains all widgets of main window
279 XFontSet fontSet, clockFontSet;
282 XFontStruct *clockFontStruct;
284 Font coordFontID, countFontID;
285 XFontStruct *coordFontStruct, *countFontStruct;
286 XtAppContext appContext;
289 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
291 Position commentX = -1, commentY = -1;
292 Dimension commentW, commentH;
293 typedef unsigned int BoardSize;
295 Boolean chessProgram;
297 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
298 int smallLayout = 0, tinyLayout = 0,
299 marginW, marginH, // [HGM] for run-time resizing
300 fromX = -1, fromY = -1, toX, toY, commentUp = False,
301 errorExitStatus = -1, defaultLineGap;
302 Dimension textHeight;
303 Pixel timerForegroundPixel, timerBackgroundPixel;
304 Pixel buttonForegroundPixel, buttonBackgroundPixel;
305 char *chessDir, *programName, *programVersion;
306 Boolean alwaysOnTop = False;
307 char *icsTextMenuString;
309 char *firstChessProgramNames;
310 char *secondChessProgramNames;
312 WindowPlacement wpMain;
313 WindowPlacement wpConsole;
314 WindowPlacement wpComment;
315 WindowPlacement wpMoveHistory;
316 WindowPlacement wpEvalGraph;
317 WindowPlacement wpEngineOutput;
318 WindowPlacement wpGameList;
319 WindowPlacement wpTags;
320 WindowPlacement wpDualBoard;
323 /* This magic number is the number of intermediate frames used
324 in each half of the animation. For short moves it's reduced
325 by 1. The total number of frames will be factor * 2 + 1. */
328 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
335 DropMenuEnables dmEnables[] = {
352 XtResource clientResources[] = {
353 { "flashCount", "flashCount", XtRInt, sizeof(int),
354 XtOffset(AppDataPtr, flashCount), XtRImmediate,
355 (XtPointer) FLASH_COUNT },
358 XrmOptionDescRec shellOptions[] = {
359 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
360 { "-flash", "flashCount", XrmoptionNoArg, "3" },
361 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
364 XtActionsRec boardActions[] = {
365 { "DrawPosition", DrawPositionProc },
366 { "HandlePV", HandlePV },
367 { "SelectPV", SelectPV },
368 { "StopPV", StopPV },
369 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
370 { "QuitProc", QuitWrapper },
371 { "ManProc", ManInner },
372 { "TempBackwardProc", TempBackwardProc },
373 { "TempForwardProc", TempForwardProc },
374 { "CommentClick", (XtActionProc) CommentClick },
375 { "GenericPopDown", (XtActionProc) GenericPopDown },
376 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
377 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
378 { "SelectMove", (XtActionProc) SelectMoveX },
379 { "LoadSelectedProc", LoadSelectedProc },
380 { "SetFilterProc", SetFilterProc },
381 { "TypeInProc", TypeInProc },
382 { "EnterKeyProc", EnterKeyProc },
383 { "UpKeyProc", UpKeyProc },
384 { "DownKeyProc", DownKeyProc },
385 { "WheelProc", WheelProc },
386 { "TabProc", TabProc },
389 char globalTranslations[] =
390 ":Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
391 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
392 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
393 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
394 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
395 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
396 :<Key>Pause: MenuItem(Mode.Pause) \n \
397 :Ctrl<Key>d: MenuItem(DebugProc) \n \
398 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
399 :<Key>Left: MenuItem(Edit.Backward) \n \
400 :<Key>Right: MenuItem(Edit.Forward) \n \
401 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
402 #ifndef OPTIONSDIALOG
404 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
405 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
406 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
407 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
408 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
411 :<KeyDown>Return: TempBackwardProc() \n \
412 :<KeyUp>Return: TempForwardProc() \n";
414 char ICSInputTranslations[] =
415 "<Key>Up: UpKeyProc() \n "
416 "<Key>Down: DownKeyProc() \n "
417 "<Key>Return: EnterKeyProc() \n";
419 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
420 // as the widget is destroyed before the up-click can call extend-end
421 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
423 String xboardResources[] = {
424 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
429 /* Max possible square size */
430 #define MAXSQSIZE 256
432 static int xpm_avail[MAXSQSIZE];
434 #ifdef HAVE_DIR_STRUCT
436 /* Extract piece size from filename */
438 xpm_getsize (char *name, int len, char *ext)
446 if ((p=strchr(name, '.')) == NULL ||
447 StrCaseCmp(p+1, ext) != 0)
453 while (*p && isdigit(*p))
460 /* Setup xpm_avail */
462 xpm_getavail (char *dirname, char *ext)
468 for (i=0; i<MAXSQSIZE; ++i)
471 if (appData.debugMode)
472 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
474 dir = opendir(dirname);
477 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
478 programName, dirname);
482 while ((ent=readdir(dir)) != NULL) {
483 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
484 if (i > 0 && i < MAXSQSIZE)
494 xpm_print_avail (FILE *fp, char *ext)
498 fprintf(fp, _("Available `%s' sizes:\n"), ext);
499 for (i=1; i<MAXSQSIZE; ++i) {
505 /* Return XPM piecesize closest to size */
507 xpm_closest_to (char *dirname, int size, char *ext)
510 int sm_diff = MAXSQSIZE;
514 xpm_getavail(dirname, ext);
516 if (appData.debugMode)
517 xpm_print_avail(stderr, ext);
519 for (i=1; i<MAXSQSIZE; ++i) {
522 diff = (diff<0) ? -diff : diff;
523 if (diff < sm_diff) {
531 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
537 #else /* !HAVE_DIR_STRUCT */
538 /* If we are on a system without a DIR struct, we can't
539 read the directory, so we can't collect a list of
540 filenames, etc., so we can't do any size-fitting. */
542 xpm_closest_to (char *dirname, int size, char *ext)
545 Warning: No DIR structure found on this system --\n\
546 Unable to autosize for XPM/XIM pieces.\n\
547 Please report this error to %s.\n\
548 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
551 #endif /* HAVE_DIR_STRUCT */
554 /* Arrange to catch delete-window events */
555 Atom wm_delete_window;
557 CatchDeleteWindow (Widget w, String procname)
560 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
561 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
562 XtAugmentTranslations(w, XtParseTranslationTable(buf));
569 XtSetArg(args[0], XtNiconic, False);
570 XtSetValues(shellWidget, args, 1);
572 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
575 //---------------------------------------------------------------------------------------------------------
576 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
579 #define CW_USEDEFAULT (1<<31)
580 #define ICS_TEXT_MENU_SIZE 90
581 #define DEBUG_FILE "xboard.debug"
582 #define SetCurrentDirectory chdir
583 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
587 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
590 // front-end part of option handling
592 // [HGM] This platform-dependent table provides the location for storing the color info
593 extern char *crWhite, * crBlack;
597 &appData.whitePieceColor,
598 &appData.blackPieceColor,
599 &appData.lightSquareColor,
600 &appData.darkSquareColor,
601 &appData.highlightSquareColor,
602 &appData.premoveHighlightColor,
603 &appData.lowTimeWarningColor,
614 // [HGM] font: keep a font for each square size, even non-stndard ones
617 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
618 char *fontTable[NUM_FONTS][MAX_SIZE];
621 ParseFont (char *name, int number)
622 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
624 if(sscanf(name, "size%d:", &size)) {
625 // [HGM] font: font is meant for specific boardSize (likely from settings file);
626 // defer processing it until we know if it matches our board size
627 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
628 fontTable[number][size] = strdup(strchr(name, ':')+1);
629 fontValid[number][size] = True;
634 case 0: // CLOCK_FONT
635 appData.clockFont = strdup(name);
637 case 1: // MESSAGE_FONT
638 appData.font = strdup(name);
640 case 2: // COORD_FONT
641 appData.coordFont = strdup(name);
646 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
651 { // only 2 fonts currently
652 appData.clockFont = CLOCK_FONT_NAME;
653 appData.coordFont = COORD_FONT_NAME;
654 appData.font = DEFAULT_FONT_NAME;
659 { // no-op, until we identify the code for this already in XBoard and move it here
663 ParseColor (int n, char *name)
664 { // in XBoard, just copy the color-name string
665 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
669 ParseTextAttribs (ColorClass cc, char *s)
671 (&appData.colorShout)[cc] = strdup(s);
675 ParseBoardSize (void *addr, char *name)
677 appData.boardSize = strdup(name);
682 { // In XBoard the sound-playing program takes care of obtaining the actual sound
686 SetCommPortDefaults ()
687 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
690 // [HGM] args: these three cases taken out to stay in front-end
692 SaveFontArg (FILE *f, ArgDescriptor *ad)
695 int i, n = (int)(intptr_t)ad->argLoc;
697 case 0: // CLOCK_FONT
698 name = appData.clockFont;
700 case 1: // MESSAGE_FONT
703 case 2: // COORD_FONT
704 name = appData.coordFont;
709 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
710 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
711 fontTable[n][squareSize] = strdup(name);
712 fontValid[n][squareSize] = True;
715 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
716 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
721 { // nothing to do, as the sounds are at all times represented by their text-string names already
725 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
726 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
727 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
731 SaveColor (FILE *f, ArgDescriptor *ad)
732 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
733 if(colorVariable[(int)(intptr_t)ad->argLoc])
734 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
738 SaveBoardSize (FILE *f, char *name, void *addr)
739 { // wrapper to shield back-end from BoardSize & sizeInfo
740 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
744 ParseCommPortSettings (char *s)
745 { // no such option in XBoard (yet)
751 GetActualPlacement (Widget wg, WindowPlacement *wp)
753 XWindowAttributes winAt;
760 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
761 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
762 wp->x = rx - winAt.x;
763 wp->y = ry - winAt.y;
764 wp->height = winAt.height;
765 wp->width = winAt.width;
766 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
771 { // wrapper to shield use of window handles from back-end (make addressible by number?)
772 // In XBoard this will have to wait until awareness of window parameters is implemented
773 GetActualPlacement(shellWidget, &wpMain);
774 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
775 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
776 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
777 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
778 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
779 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
783 PrintCommPortSettings (FILE *f, char *name)
784 { // This option does not exist in XBoard
788 EnsureOnScreen (int *x, int *y, int minX, int minY)
795 { // [HGM] args: allows testing if main window is realized from back-end
796 return xBoardWindow != 0;
800 PopUpStartupDialog ()
801 { // start menu not implemented in XBoard
805 ConvertToLine (int argc, char **argv)
807 static char line[128*1024], buf[1024];
811 for(i=1; i<argc; i++)
813 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
814 && argv[i][0] != '{' )
815 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
817 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
818 strncat(line, buf, 128*1024 - strlen(line) - 1 );
821 line[strlen(line)-1] = NULLCHAR;
825 //--------------------------------------------------------------------------------------------
828 ResizeBoardWindow (int w, int h, int inhibit)
830 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
832 shellArgs[0].value = w;
833 shellArgs[1].value = h;
834 shellArgs[4].value = shellArgs[2].value = w;
835 shellArgs[5].value = shellArgs[3].value = h;
836 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
838 XSync(xDisplay, False);
842 MakeOneColor (char *name, Pixel *color)
845 if (!appData.monoMode) {
846 vFrom.addr = (caddr_t) name;
847 vFrom.size = strlen(name);
848 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
849 if (vTo.addr == NULL) {
850 appData.monoMode = True;
853 *color = *(Pixel *) vTo.addr;
861 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
862 int forceMono = False;
864 if (appData.lowTimeWarning)
865 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
866 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
867 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
873 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
874 { // detervtomine what fonts to use, and create them
878 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
879 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
880 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
881 appData.font = fontTable[MESSAGE_FONT][squareSize];
882 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
883 appData.coordFont = fontTable[COORD_FONT][squareSize];
886 appData.font = InsertPxlSize(appData.font, fontPxlSize);
887 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
888 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
889 fontSet = CreateFontSet(appData.font);
890 clockFontSet = CreateFontSet(appData.clockFont);
892 /* For the coordFont, use the 0th font of the fontset. */
893 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
894 XFontStruct **font_struct_list;
895 XFontSetExtents *fontSize;
896 char **font_name_list;
897 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
898 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
899 coordFontStruct = XQueryFont(xDisplay, coordFontID);
900 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
901 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
904 appData.font = FindFont(appData.font, fontPxlSize);
905 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
906 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
907 clockFontID = XLoadFont(xDisplay, appData.clockFont);
908 clockFontStruct = XQueryFont(xDisplay, clockFontID);
909 coordFontID = XLoadFont(xDisplay, appData.coordFont);
910 coordFontStruct = XQueryFont(xDisplay, coordFontID);
911 // textHeight in !NLS mode!
913 countFontID = coordFontID; // [HGM] holdings
914 countFontStruct = coordFontStruct;
916 xdb = XtDatabase(xDisplay);
918 XrmPutLineResource(&xdb, "*international: True");
919 vTo.size = sizeof(XFontSet);
920 vTo.addr = (XtPointer) &fontSet;
921 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
923 XrmPutStringResource(&xdb, "*font", appData.font);
933 case ArgInt: p = " N"; break;
934 case ArgString: p = " STR"; break;
935 case ArgBoolean: p = " TF"; break;
936 case ArgSettingsFilename:
937 case ArgFilename: p = " FILE"; break;
938 case ArgX: p = " Nx"; break;
939 case ArgY: p = " Ny"; break;
940 case ArgAttribs: p = " TEXTCOL"; break;
941 case ArgColor: p = " COL"; break;
942 case ArgFont: p = " FONT"; break;
943 case ArgBoardSize: p = " SIZE"; break;
944 case ArgFloat: p = " FLOAT"; break;
949 case ArgCommSettings:
956 GenerateGlobalTranslationTable (void)
958 /* go through all menu items and extract the keyboard shortcuts, so that X11 can load them */
966 /* loop over all menu entries */
967 for( i=0; menuBar[i].mi ; i++)
970 for(j=0; mi[j].proc; j++)
978 char *key,*test, *mods;
980 /* check for Ctrl/Alt */
981 if( strstr(mi[j].accel, "<Ctrl>") ) ctrl = 1;
982 if( strstr(mi[j].accel, "<Shift>") ) shift = 1;
983 if( strstr(mi[j].accel, "<Alt>") ) alt = 1;
985 /* remove all <...> */
986 test = strrchr(mi[j].accel, '>');
988 key = strdup(mi[j].accel);
990 key = strdup(++test); // remove ">"
992 /* instead of shift X11 uses the uppercase letter directly*/
993 if (shift && strlen(key)==1 )
995 *key = toupper(*key);
999 /* handle some special cases which have different names in X11 */
1000 if ( strncmp(key, "Page_Down", 9) == 0 )
1005 else if ( strncmp(key, "Page_Up", 7) == 0 )
1008 key=strdup("Prior");
1011 /* create string of mods */
1013 mods = strdup("Ctrl ");
1019 mods = realloc(mods, strlen(mods) + strlen("Meta ")+1);
1020 strncat(mods, "Meta ", 5);
1025 mods = realloc(mods, strlen(mods) + strlen("Shift ")+1);
1026 strncat(mods, "Shift ", 6);
1029 // remove trailing space
1030 if( isspace(mods[strlen(mods)-1]) )
1031 mods[strlen(mods)-1]='\0';
1033 /* get the name for the callback, we can use MenuItem() here that will call KeyBindingProc */
1034 size_t namesize = snprintf(NULL, 0, "%s.%s", menuBar[i].ref, mi[j].ref);
1035 char *name = malloc(namesize+1);
1036 snprintf(name, namesize+1, "%s.%s", menuBar[i].ref, mi[j].ref);
1038 size_t buffersize = snprintf(NULL, 0, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
1039 char *buffer = malloc(buffersize+1);
1040 snprintf(buffer, buffersize+1, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
1042 /* add string to the output */
1043 output = realloc(output, strlen(output) + strlen(buffer)+1);
1044 strncat(output, buffer, strlen(buffer));
1063 ArgDescriptor *q, *p = argDescriptors+5;
1064 printf("\nXBoard accepts the following options:\n"
1065 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1066 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1067 " SIZE = board-size spec(s)\n"
1068 " Within parentheses are short forms, or options to set to true or false.\n"
1069 " Persistent options (saved in the settings file) are marked with *)\n\n");
1071 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1072 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1073 if(p->save) strcat(buf+len, "*");
1074 for(q=p+1; q->argLoc == p->argLoc; q++) {
1075 if(q->argName[0] == '-') continue;
1076 strcat(buf+len, q == p+1 ? " (" : " ");
1077 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1079 if(q != p+1) strcat(buf+len, ")");
1081 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1084 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1088 SlaveResize (Option *opt)
1093 main (int argc, char **argv)
1095 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1096 XSetWindowAttributes window_attributes;
1098 Dimension boardWidth, boardHeight, w, h;
1100 int forceMono = False;
1102 srandom(time(0)); // [HGM] book: make random truly random
1104 setbuf(stdout, NULL);
1105 setbuf(stderr, NULL);
1108 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1109 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1113 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1118 programName = strrchr(argv[0], '/');
1119 if (programName == NULL)
1120 programName = argv[0];
1125 XtSetLanguageProc(NULL, NULL, NULL);
1126 if (appData.debugMode) {
1127 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1130 bindtextdomain(PACKAGE, LOCALEDIR);
1131 textdomain(PACKAGE);
1134 appData.boardSize = "";
1135 InitAppData(ConvertToLine(argc, argv));
1137 if (p == NULL) p = "/tmp";
1138 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1139 gameCopyFilename = (char*) malloc(i);
1140 gamePasteFilename = (char*) malloc(i);
1141 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1142 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1144 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1145 static char buf[MSG_SIZ];
1146 EscapeExpand(buf, appData.firstInitString);
1147 appData.firstInitString = strdup(buf);
1148 EscapeExpand(buf, appData.secondInitString);
1149 appData.secondInitString = strdup(buf);
1150 EscapeExpand(buf, appData.firstComputerString);
1151 appData.firstComputerString = strdup(buf);
1152 EscapeExpand(buf, appData.secondComputerString);
1153 appData.secondComputerString = strdup(buf);
1156 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1159 if (chdir(chessDir) != 0) {
1160 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1166 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1167 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1168 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1169 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1172 setbuf(debugFP, NULL);
1175 /* [HGM,HR] make sure board size is acceptable */
1176 if(appData.NrFiles > BOARD_FILES ||
1177 appData.NrRanks > BOARD_RANKS )
1178 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1181 /* This feature does not work; animation needs a rewrite */
1182 appData.highlightDragging = FALSE;
1186 gameInfo.variant = StringToVariant(appData.variant);
1187 InitPosition(FALSE);
1190 XtAppInitialize(&appContext, "XBoard", shellOptions,
1191 XtNumber(shellOptions),
1192 &argc, argv, xboardResources, NULL, 0);
1194 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1195 clientResources, XtNumber(clientResources),
1198 xDisplay = XtDisplay(shellWidget);
1199 xScreen = DefaultScreen(xDisplay);
1200 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1203 * determine size, based on supplied or remembered -size, or screen size
1205 if (isdigit(appData.boardSize[0])) {
1206 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1207 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1208 &fontPxlSize, &smallLayout, &tinyLayout);
1210 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1211 programName, appData.boardSize);
1215 /* Find some defaults; use the nearest known size */
1216 SizeDefaults *szd, *nearest;
1217 int distance = 99999;
1218 nearest = szd = sizeDefaults;
1219 while (szd->name != NULL) {
1220 if (abs(szd->squareSize - squareSize) < distance) {
1222 distance = abs(szd->squareSize - squareSize);
1223 if (distance == 0) break;
1227 if (i < 2) lineGap = nearest->lineGap;
1228 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1229 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1230 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1231 if (i < 6) smallLayout = nearest->smallLayout;
1232 if (i < 7) tinyLayout = nearest->tinyLayout;
1235 SizeDefaults *szd = sizeDefaults;
1236 if (*appData.boardSize == NULLCHAR) {
1237 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1238 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1241 if (szd->name == NULL) szd--;
1242 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1244 while (szd->name != NULL &&
1245 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1246 if (szd->name == NULL) {
1247 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1248 programName, appData.boardSize);
1252 squareSize = szd->squareSize;
1253 lineGap = szd->lineGap;
1254 clockFontPxlSize = szd->clockFontPxlSize;
1255 coordFontPxlSize = szd->coordFontPxlSize;
1256 fontPxlSize = szd->fontPxlSize;
1257 smallLayout = szd->smallLayout;
1258 tinyLayout = szd->tinyLayout;
1259 // [HGM] font: use defaults from settings file if available and not overruled
1262 defaultLineGap = lineGap;
1263 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1265 /* [HR] height treated separately (hacked) */
1266 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1267 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1270 * Determine what fonts to use.
1272 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1275 * Detect if there are not enough colors available and adapt.
1277 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1278 appData.monoMode = True;
1281 forceMono = MakeColors();
1284 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1286 appData.monoMode = True;
1289 if (appData.monoMode && appData.debugMode) {
1290 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1291 (unsigned long) XWhitePixel(xDisplay, xScreen),
1292 (unsigned long) XBlackPixel(xDisplay, xScreen));
1295 ParseIcsTextColors();
1297 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1303 layoutName = "tinyLayout";
1304 } else if (smallLayout) {
1305 layoutName = "smallLayout";
1307 layoutName = "normalLayout";
1310 optList = BoardPopUp(squareSize, lineGap, (void*)
1316 InitDrawingHandle(optList + W_BOARD);
1317 currBoard = &optList[W_BOARD];
1318 boardWidget = optList[W_BOARD].handle;
1319 menuBarWidget = optList[W_MENU].handle;
1320 dropMenu = optList[W_DROP].handle;
1321 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1322 formWidget = XtParent(boardWidget);
1323 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1324 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1325 XtGetValues(optList[W_WHITE].handle, args, 2);
1326 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1327 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1328 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1329 XtGetValues(optList[W_PAUSE].handle, args, 2);
1332 xBoardWindow = XtWindow(boardWidget);
1334 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1335 // not need to go into InitDrawingSizes().
1338 * Create X checkmark bitmap and initialize option menu checks.
1340 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1341 checkmark_bits, checkmark_width, checkmark_height);
1347 ReadBitmap(&wIconPixmap, "icon_white.bm",
1348 icon_white_bits, icon_white_width, icon_white_height);
1349 ReadBitmap(&bIconPixmap, "icon_black.bm",
1350 icon_black_bits, icon_black_width, icon_black_height);
1351 iconPixmap = wIconPixmap;
1353 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1354 XtSetValues(shellWidget, args, i);
1357 * Create a cursor for the board widget.
1359 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1360 XChangeWindowAttributes(xDisplay, xBoardWindow,
1361 CWCursor, &window_attributes);
1364 * Inhibit shell resizing.
1366 shellArgs[0].value = (XtArgVal) &w;
1367 shellArgs[1].value = (XtArgVal) &h;
1368 XtGetValues(shellWidget, shellArgs, 2);
1369 shellArgs[4].value = shellArgs[2].value = w;
1370 shellArgs[5].value = shellArgs[3].value = h;
1371 // XtSetValues(shellWidget, &shellArgs[2], 4);
1372 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1373 marginH = h - boardHeight;
1375 CatchDeleteWindow(shellWidget, "QuitProc");
1380 if(appData.logoSize)
1381 { // locate and read user logo
1383 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1384 ASSIGN(userLogo, buf);
1387 if (appData.animate || appData.animateDragging)
1391 char *TranslationsTableMenus=GenerateGlobalTranslationTable ();
1393 XtAugmentTranslations(formWidget,
1394 XtParseTranslationTable(globalTranslations));
1395 XtAugmentTranslations(formWidget,
1396 XtParseTranslationTable(TranslationsTableMenus));
1398 XtAddEventHandler(formWidget, KeyPressMask, False,
1399 (XtEventHandler) MoveTypeInProc, NULL);
1400 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1401 (XtEventHandler) EventProc, NULL);
1403 /* [AS] Restore layout */
1404 if( wpMoveHistory.visible ) {
1408 if( wpEvalGraph.visible )
1413 if( wpEngineOutput.visible ) {
1414 EngineOutputPopUp();
1419 if (errorExitStatus == -1) {
1420 if (appData.icsActive) {
1421 /* We now wait until we see "login:" from the ICS before
1422 sending the logon script (problems with timestamp otherwise) */
1423 /*ICSInitScript();*/
1424 if (appData.icsInputBox) ICSInputBoxPopUp();
1428 signal(SIGWINCH, TermSizeSigHandler);
1430 signal(SIGINT, IntSigHandler);
1431 signal(SIGTERM, IntSigHandler);
1432 if (*appData.cmailGameName != NULLCHAR) {
1433 signal(SIGUSR1, CmailSigHandler);
1437 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1440 // XtSetKeyboardFocus(shellWidget, formWidget);
1441 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1443 XtAppMainLoop(appContext);
1444 if (appData.debugMode) fclose(debugFP); // [DM] debug
1449 TermSizeSigHandler (int sig)
1455 IntSigHandler (int sig)
1461 CmailSigHandler (int sig)
1466 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1468 /* Activate call-back function CmailSigHandlerCallBack() */
1469 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1471 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1475 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1478 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1480 /**** end signal code ****/
1483 #define Abs(n) ((n)<0 ? -(n) : (n))
1487 InsertPxlSize (char *pattern, int targetPxlSize)
1489 char *base_fnt_lst, strInt[12], *p, *q;
1490 int alternatives, i, len, strIntLen;
1493 * Replace the "*" (if present) in the pixel-size slot of each
1494 * alternative with the targetPxlSize.
1498 while ((p = strchr(p, ',')) != NULL) {
1502 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1503 strIntLen = strlen(strInt);
1504 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1508 while (alternatives--) {
1509 char *comma = strchr(p, ',');
1510 for (i=0; i<14; i++) {
1511 char *hyphen = strchr(p, '-');
1513 if (comma && hyphen > comma) break;
1514 len = hyphen + 1 - p;
1515 if (i == 7 && *p == '*' && len == 2) {
1517 memcpy(q, strInt, strIntLen);
1527 len = comma + 1 - p;
1534 return base_fnt_lst;
1538 CreateFontSet (char *base_fnt_lst)
1541 char **missing_list;
1545 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1546 &missing_list, &missing_count, &def_string);
1547 if (appData.debugMode) {
1549 XFontStruct **font_struct_list;
1550 char **font_name_list;
1551 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1553 fprintf(debugFP, " got list %s, locale %s\n",
1554 XBaseFontNameListOfFontSet(fntSet),
1555 XLocaleOfFontSet(fntSet));
1556 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1557 for (i = 0; i < count; i++) {
1558 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1561 for (i = 0; i < missing_count; i++) {
1562 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1565 if (fntSet == NULL) {
1566 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1571 #else // not ENABLE_NLS
1573 * Find a font that matches "pattern" that is as close as
1574 * possible to the targetPxlSize. Prefer fonts that are k
1575 * pixels smaller to fonts that are k pixels larger. The
1576 * pattern must be in the X Consortium standard format,
1577 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1578 * The return value should be freed with XtFree when no
1582 FindFont (char *pattern, int targetPxlSize)
1584 char **fonts, *p, *best, *scalable, *scalableTail;
1585 int i, j, nfonts, minerr, err, pxlSize;
1587 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1589 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1590 programName, pattern);
1597 for (i=0; i<nfonts; i++) {
1600 if (*p != '-') continue;
1602 if (*p == NULLCHAR) break;
1603 if (*p++ == '-') j++;
1605 if (j < 7) continue;
1608 scalable = fonts[i];
1611 err = pxlSize - targetPxlSize;
1612 if (Abs(err) < Abs(minerr) ||
1613 (minerr > 0 && err < 0 && -err == minerr)) {
1619 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1620 /* If the error is too big and there is a scalable font,
1621 use the scalable font. */
1622 int headlen = scalableTail - scalable;
1623 p = (char *) XtMalloc(strlen(scalable) + 10);
1624 while (isdigit(*scalableTail)) scalableTail++;
1625 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1627 p = (char *) XtMalloc(strlen(best) + 2);
1628 safeStrCpy(p, best, strlen(best)+1 );
1630 if (appData.debugMode) {
1631 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1632 pattern, targetPxlSize, p);
1634 XFreeFontNames(fonts);
1640 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1643 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1649 MarkMenuItem (char *menuRef, int state)
1651 MenuItem *item = MenuNameToItem(menuRef);
1655 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1656 XtSetValues(item->handle, args, 1);
1661 EnableNamedMenuItem (char *menuRef, int state)
1663 MenuItem *item = MenuNameToItem(menuRef);
1665 if(item) XtSetSensitive(item->handle, state);
1669 EnableButtonBar (int state)
1671 XtSetSensitive(optList[W_BUTTON].handle, state);
1676 SetMenuEnables (Enables *enab)
1678 while (enab->name != NULL) {
1679 EnableNamedMenuItem(enab->name, enab->value);
1685 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1686 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1688 if(*nprms == 0) return;
1689 item = MenuNameToItem(prms[0]);
1690 if(item) ((MenuProc *) item->proc) ();
1702 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1703 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1704 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1705 dmEnables[i].piece);
1706 XtSetSensitive(entry, p != NULL || !appData.testLegality
1707 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1708 && !appData.icsActive));
1710 while (p && *p++ == dmEnables[i].piece) count++;
1711 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1713 XtSetArg(args[j], XtNlabel, label); j++;
1714 XtSetValues(entry, args, j);
1719 do_flash_delay (unsigned long msec)
1725 FlashDelay (int flash_delay)
1727 XSync(xDisplay, False);
1728 if(flash_delay) do_flash_delay(flash_delay);
1732 Fraction (int x, int start, int stop)
1734 double f = ((double) x - start)/(stop - start);
1735 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1739 static WindowPlacement wpNew;
1742 CoDrag (Widget sh, WindowPlacement *wp)
1745 int j=0, touch=0, fudge = 2;
1746 GetActualPlacement(sh, wp);
1747 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1748 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1749 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1750 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1751 if(!touch ) return; // only windows that touch co-move
1752 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1753 int heightInc = wpNew.height - wpMain.height;
1754 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1755 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1756 wp->y += fracTop * heightInc;
1757 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1758 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1759 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1760 int widthInc = wpNew.width - wpMain.width;
1761 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1762 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1763 wp->y += fracLeft * widthInc;
1764 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1765 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1767 wp->x += wpNew.x - wpMain.x;
1768 wp->y += wpNew.y - wpMain.y;
1769 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1770 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1771 XtSetArg(args[j], XtNx, wp->x); j++;
1772 XtSetArg(args[j], XtNy, wp->y); j++;
1773 XtSetValues(sh, args, j);
1777 ReSize (WindowPlacement *wp)
1780 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1781 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1782 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1783 if(sqy < sqx) sqx = sqy;
1784 if(sqx != squareSize) {
1785 squareSize = sqx; // adopt new square size
1786 CreatePNGPieces(); // make newly scaled pieces
1787 InitDrawingSizes(0, 0); // creates grid etc.
1788 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1789 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1790 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1791 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1792 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1795 static XtIntervalId delayedDragID = 0;
1804 GetActualPlacement(shellWidget, &wpNew);
1805 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1806 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1807 busy = 0; return; // false alarm
1810 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1811 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1812 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1813 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1815 DrawPosition(True, NULL);
1816 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1824 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1826 XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1830 EventProc (Widget widget, caddr_t unused, XEvent *event)
1832 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1833 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1837 * event handler for redrawing the board
1840 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1842 DrawPosition(True, NULL);
1847 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1848 { // [HGM] pv: walk PV
1849 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1852 extern int savedIndex; /* gross that this is global */
1855 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1858 XawTextPosition index, dummy;
1861 XawTextGetSelectionPos(w, &index, &dummy);
1862 XtSetArg(arg, XtNstring, &val);
1863 XtGetValues(w, &arg, 1);
1864 ReplaceComment(savedIndex, val);
1865 if(savedIndex != currentMove) ToNrEvent(savedIndex);
1866 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1870 /* Disable all user input other than deleting the window */
1871 static int frozen = 0;
1877 /* Grab by a widget that doesn't accept input */
1878 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1882 /* Undo a FreezeUI */
1886 if (!frozen) return;
1887 XtRemoveGrab(optList[W_MESSG].handle);
1895 static int oldPausing = FALSE;
1896 static GameMode oldmode = (GameMode) -1;
1899 if (!boardWidget || !XtIsRealized(boardWidget)) return;
1901 if (pausing != oldPausing) {
1902 oldPausing = pausing;
1903 MarkMenuItem("Mode.Pause", pausing);
1905 if (appData.showButtonBar) {
1906 /* Always toggle, don't set. Previous code messes up when
1907 invoked while the button is pressed, as releasing it
1908 toggles the state again. */
1911 XtSetArg(args[0], XtNbackground, &oldbg);
1912 XtSetArg(args[1], XtNforeground, &oldfg);
1913 XtGetValues(optList[W_PAUSE].handle,
1915 XtSetArg(args[0], XtNbackground, oldfg);
1916 XtSetArg(args[1], XtNforeground, oldbg);
1918 XtSetValues(optList[W_PAUSE].handle, args, 2);
1922 wname = ModeToWidgetName(oldmode);
1923 if (wname != NULL) {
1924 MarkMenuItem(wname, False);
1926 wname = ModeToWidgetName(gameMode);
1927 if (wname != NULL) {
1928 MarkMenuItem(wname, True);
1931 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1933 /* Maybe all the enables should be handled here, not just this one */
1934 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1936 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1941 * Button/menu procedures
1944 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1945 char *selected_fen_position=NULL;
1948 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1949 Atom *type_return, XtPointer *value_return,
1950 unsigned long *length_return, int *format_return)
1952 char *selection_tmp;
1954 // if (!selected_fen_position) return False; /* should never happen */
1955 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1956 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1957 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1960 if (f == NULL) return False;
1964 selection_tmp = XtMalloc(len + 1);
1965 count = fread(selection_tmp, 1, len, f);
1968 XtFree(selection_tmp);
1971 selection_tmp[len] = NULLCHAR;
1973 /* note: since no XtSelectionDoneProc was registered, Xt will
1974 * automatically call XtFree on the value returned. So have to
1975 * make a copy of it allocated with XtMalloc */
1976 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1977 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1980 *value_return=selection_tmp;
1981 *length_return=strlen(selection_tmp);
1982 *type_return=*target;
1983 *format_return = 8; /* bits per byte */
1985 } else if (*target == XA_TARGETS(xDisplay)) {
1986 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1987 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1988 targets_tmp[1] = XA_STRING;
1989 *value_return = targets_tmp;
1990 *type_return = XA_ATOM;
1993 // This code leads to a read of value_return out of bounds on 64-bit systems.
1994 // Other code which I have seen always sets *format_return to 32 independent of
1995 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1996 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1997 *format_return = 8 * sizeof(Atom);
1998 if (*format_return > 32) {
1999 *length_return *= *format_return / 32;
2000 *format_return = 32;
2003 *format_return = 32;
2011 /* note: when called from menu all parameters are NULL, so no clue what the
2012 * Widget which was clicked on was, or what the click event was
2015 CopySomething (char *src)
2017 selected_fen_position = src;
2019 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2020 * have a notion of a position that is selected but not copied.
2021 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2023 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2025 SendPositionSelection,
2026 NULL/* lose_ownership_proc */ ,
2027 NULL/* transfer_done_proc */);
2028 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2030 SendPositionSelection,
2031 NULL/* lose_ownership_proc */ ,
2032 NULL/* transfer_done_proc */);
2035 /* function called when the data to Paste is ready */
2037 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2038 Atom *type, XtPointer value, unsigned long *len, int *format)
2041 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2042 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2043 EditPositionPasteFEN(fenstr);
2047 /* called when Paste Position button is pressed,
2048 * all parameters will be NULL */
2050 PastePositionProc ()
2052 XtGetSelectionValue(menuBarWidget,
2053 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2054 /* (XtSelectionCallbackProc) */ PastePositionCB,
2055 NULL, /* client_data passed to PastePositionCB */
2057 /* better to use the time field from the event that triggered the
2058 * call to this function, but that isn't trivial to get
2065 /* note: when called from menu all parameters are NULL, so no clue what the
2066 * Widget which was clicked on was, or what the click event was
2068 /* function called when the data to Paste is ready */
2070 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2071 Atom *type, XtPointer value, unsigned long *len, int *format)
2074 if (value == NULL || *len == 0) {
2075 return; /* nothing had been selected to copy */
2077 f = fopen(gamePasteFilename, "w");
2079 DisplayError(_("Can't open temp file"), errno);
2082 fwrite(value, 1, *len, f);
2085 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2088 /* called when Paste Game button is pressed,
2089 * all parameters will be NULL */
2093 XtGetSelectionValue(menuBarWidget,
2094 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2095 /* (XtSelectionCallbackProc) */ PasteGameCB,
2096 NULL, /* client_data passed to PasteGameCB */
2098 /* better to use the time field from the event that triggered the
2099 * call to this function, but that isn't trivial to get
2108 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2115 { // bassic primitive for determining if modifier keys are pressed
2116 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2119 XQueryKeymap(xDisplay,keys);
2120 for(i=0; i<6; i++) {
2122 j = XKeysymToKeycode(xDisplay, codes[i]);
2123 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2129 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2133 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2134 if ( n == 1 && *buf >= 32 // printable
2135 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2136 ) BoxAutoPopUp (buf);
2140 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2141 { // [HGM] input: let up-arrow recall previous line from history
2146 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2147 { // [HGM] input: let down-arrow recall next line from history
2152 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2158 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2160 if (!TempBackwardActive) {
2161 TempBackwardActive = True;
2167 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2169 /* Check to see if triggered by a key release event for a repeating key.
2170 * If so the next queued event will be a key press of the same key at the same time */
2171 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2173 XPeekEvent(xDisplay, &next);
2174 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2175 next.xkey.keycode == event->xkey.keycode)
2179 TempBackwardActive = False;
2183 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2184 { // called as key binding
2187 if (nprms && *nprms > 0)
2191 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2197 { // called from menu
2198 ManInner(NULL, NULL, NULL, NULL);
2202 SetWindowTitle (char *text, char *title, char *icon)
2206 if (appData.titleInWindow) {
2208 XtSetArg(args[i], XtNlabel, text); i++;
2209 XtSetValues(titleWidget, args, i);
2212 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2213 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2214 XtSetValues(shellWidget, args, i);
2215 XSync(xDisplay, False);
2220 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2226 DisplayIcsInteractionTitle (String message)
2228 if (oldICSInteractionTitle == NULL) {
2229 /* Magic to find the old window title, adapted from vim */
2230 char *wina = getenv("WINDOWID");
2232 Window win = (Window) atoi(wina);
2233 Window root, parent, *children;
2234 unsigned int nchildren;
2235 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2237 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2238 if (!XQueryTree(xDisplay, win, &root, &parent,
2239 &children, &nchildren)) break;
2240 if (children) XFree((void *)children);
2241 if (parent == root || parent == 0) break;
2244 XSetErrorHandler(oldHandler);
2246 if (oldICSInteractionTitle == NULL) {
2247 oldICSInteractionTitle = "xterm";
2250 printf("\033]0;%s\007", message);
2255 XtIntervalId delayedEventTimerXID = 0;
2256 DelayedEventCallback delayedEventCallback = 0;
2261 delayedEventTimerXID = 0;
2262 delayedEventCallback();
2266 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2268 if(delayedEventTimerXID && delayedEventCallback == cb)
2269 // [HGM] alive: replace, rather than add or flush identical event
2270 XtRemoveTimeOut(delayedEventTimerXID);
2271 delayedEventCallback = cb;
2272 delayedEventTimerXID =
2273 XtAppAddTimeOut(appContext, millisec,
2274 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2277 DelayedEventCallback
2280 if (delayedEventTimerXID) {
2281 return delayedEventCallback;
2288 CancelDelayedEvent ()
2290 if (delayedEventTimerXID) {
2291 XtRemoveTimeOut(delayedEventTimerXID);
2292 delayedEventTimerXID = 0;
2296 XtIntervalId loadGameTimerXID = 0;
2299 LoadGameTimerRunning ()
2301 return loadGameTimerXID != 0;
2305 StopLoadGameTimer ()
2307 if (loadGameTimerXID != 0) {
2308 XtRemoveTimeOut(loadGameTimerXID);
2309 loadGameTimerXID = 0;
2317 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2319 loadGameTimerXID = 0;
2324 StartLoadGameTimer (long millisec)
2327 XtAppAddTimeOut(appContext, millisec,
2328 (XtTimerCallbackProc) LoadGameTimerCallback,
2332 XtIntervalId analysisClockXID = 0;
2335 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2337 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2338 || appData.icsEngineAnalyze) { // [DM]
2339 AnalysisPeriodicEvent(0);
2340 StartAnalysisClock();
2345 StartAnalysisClock ()
2348 XtAppAddTimeOut(appContext, 2000,
2349 (XtTimerCallbackProc) AnalysisClockCallback,
2353 XtIntervalId clockTimerXID = 0;
2356 ClockTimerRunning ()
2358 return clockTimerXID != 0;
2364 if (clockTimerXID != 0) {
2365 XtRemoveTimeOut(clockTimerXID);
2374 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2381 StartClockTimer (long millisec)
2384 XtAppAddTimeOut(appContext, millisec,
2385 (XtTimerCallbackProc) ClockTimerCallback,
2390 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2394 Widget w = (Widget) opt->handle;
2396 /* check for low time warning */
2397 Pixel foregroundOrWarningColor = timerForegroundPixel;
2400 appData.lowTimeWarning &&
2401 (timer / 1000) < appData.icsAlarmTime)
2402 foregroundOrWarningColor = lowTimeWarningColor;
2404 if (appData.clockMode) {
2405 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2406 XtSetArg(args[0], XtNlabel, buf);
2408 snprintf(buf, MSG_SIZ, "%s ", color);
2409 XtSetArg(args[0], XtNlabel, buf);
2414 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2415 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2417 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2418 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2421 XtSetValues(w, args, 3);
2424 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2427 SetClockIcon (int color)
2430 Pixmap pm = *clockIcons[color];
2431 if (iconPixmap != pm) {
2433 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2434 XtSetValues(shellWidget, args, 1);
2438 #define INPUT_SOURCE_BUF_SIZE 8192
2447 char buf[INPUT_SOURCE_BUF_SIZE];
2452 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2454 InputSource *is = (InputSource *) closure;
2459 if (is->lineByLine) {
2460 count = read(is->fd, is->unused,
2461 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2463 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2466 is->unused += count;
2468 while (p < is->unused) {
2469 q = memchr(p, '\n', is->unused - p);
2470 if (q == NULL) break;
2472 (is->func)(is, is->closure, p, q - p, 0);
2476 while (p < is->unused) {
2481 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2486 (is->func)(is, is->closure, is->buf, count, error);
2491 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2494 ChildProc *cp = (ChildProc *) pr;
2496 is = (InputSource *) calloc(1, sizeof(InputSource));
2497 is->lineByLine = lineByLine;
2501 is->fd = fileno(stdin);
2503 is->kind = cp->kind;
2504 is->fd = cp->fdFrom;
2507 is->unused = is->buf;
2510 is->xid = XtAppAddInput(appContext, is->fd,
2511 (XtPointer) (XtInputReadMask),
2512 (XtInputCallbackProc) DoInputCallback,
2514 is->closure = closure;
2515 return (InputSourceRef) is;
2519 RemoveInputSource (InputSourceRef isr)
2521 InputSource *is = (InputSource *) isr;
2523 if (is->xid == 0) return;
2524 XtRemoveInput(is->xid);
2530 static Boolean frameWaiting;
2533 FrameAlarm (int sig)
2535 frameWaiting = False;
2536 /* In case System-V style signals. Needed?? */
2537 signal(SIGALRM, FrameAlarm);
2541 FrameDelay (int time)
2543 struct itimerval delay;
2545 XSync(xDisplay, False);
2548 frameWaiting = True;
2549 signal(SIGALRM, FrameAlarm);
2550 delay.it_interval.tv_sec =
2551 delay.it_value.tv_sec = time / 1000;
2552 delay.it_interval.tv_usec =
2553 delay.it_value.tv_usec = (time % 1000) * 1000;
2554 setitimer(ITIMER_REAL, &delay, NULL);
2555 while (frameWaiting) pause();
2556 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2557 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2558 setitimer(ITIMER_REAL, &delay, NULL);
2565 FrameDelay (int time)
2567 XSync(xDisplay, False);
2569 usleep(time * 1000);
2575 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2577 char buf[MSG_SIZ], *logoName = buf;
2578 if(appData.logo[n][0]) {
2579 logoName = appData.logo[n];
2580 } else if(appData.autoLogo) {
2581 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2582 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2583 } else if(appData.directory[n] && appData.directory[n][0]) {
2584 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2588 { ASSIGN(cps->programLogo, logoName); }
2592 UpdateLogos (int displ)
2594 if(optList[W_WHITE-1].handle == NULL) return;
2595 LoadLogo(&first, 0, 0);
2596 LoadLogo(&second, 1, appData.icsActive);
2597 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);