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 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 ICSInitScript P((void));
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;
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 ":<Key>F9: MenuItem(Actions.Resign) \n \
391 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
392 :Meta<Key>V: MenuItem(File.NewVariant) \n \
393 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
394 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
395 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
396 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
397 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
398 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
399 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
400 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
401 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
402 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
403 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
404 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
405 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
406 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
407 :Ctrl<Key>q: MenuItem(File.Quit) \n \
408 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
409 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
410 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
411 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
412 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
413 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
414 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
415 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
416 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
417 :Meta<Key>G: MenuItem(View.GameList) \n \
418 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
419 :<Key>Pause: MenuItem(Mode.Pause) \n \
420 :<Key>F3: MenuItem(Action.Accept) \n \
421 :<Key>F4: MenuItem(Action.Decline) \n \
422 :<Key>F12: MenuItem(Action.Rematch) \n \
423 :<Key>F5: MenuItem(Action.CallFlag) \n \
424 :<Key>F6: MenuItem(Action.Draw) \n \
425 :<Key>F7: MenuItem(Action.Adjourn) \n \
426 :<Key>F8: MenuItem(Action.Abort) \n \
427 :<Key>F10: MenuItem(Action.StopObserving) \n \
428 :<Key>F11: MenuItem(Action.StopExamining) \n \
429 :Ctrl<Key>d: MenuItem(DebugProc) \n \
430 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
431 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
432 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
433 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
434 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
435 :<Key>Left: MenuItem(Edit.Backward) \n \
436 :<Key>Right: MenuItem(Edit.Forward) \n \
437 :<Key>Home: MenuItem(Edit.Revert) \n \
438 :<Key>End: MenuItem(Edit.TruncateGame) \n \
439 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
440 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
441 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
442 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
443 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
444 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
445 #ifndef OPTIONSDIALOG
447 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
448 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
449 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
450 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
451 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
454 :<Key>F1: MenuItem(Help.ManXBoard) \n \
455 :<Key>F2: MenuItem(View.FlipView) \n \
456 :<KeyDown>Return: TempBackwardProc() \n \
457 :<KeyUp>Return: TempForwardProc() \n";
459 char ICSInputTranslations[] =
460 "<Key>Up: UpKeyProc() \n "
461 "<Key>Down: DownKeyProc() \n "
462 "<Key>Return: EnterKeyProc() \n";
464 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
465 // as the widget is destroyed before the up-click can call extend-end
466 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
468 String xboardResources[] = {
469 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
474 /* Max possible square size */
475 #define MAXSQSIZE 256
477 static int xpm_avail[MAXSQSIZE];
479 #ifdef HAVE_DIR_STRUCT
481 /* Extract piece size from filename */
483 xpm_getsize (char *name, int len, char *ext)
491 if ((p=strchr(name, '.')) == NULL ||
492 StrCaseCmp(p+1, ext) != 0)
498 while (*p && isdigit(*p))
505 /* Setup xpm_avail */
507 xpm_getavail (char *dirname, char *ext)
513 for (i=0; i<MAXSQSIZE; ++i)
516 if (appData.debugMode)
517 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
519 dir = opendir(dirname);
522 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
523 programName, dirname);
527 while ((ent=readdir(dir)) != NULL) {
528 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
529 if (i > 0 && i < MAXSQSIZE)
539 xpm_print_avail (FILE *fp, char *ext)
543 fprintf(fp, _("Available `%s' sizes:\n"), ext);
544 for (i=1; i<MAXSQSIZE; ++i) {
550 /* Return XPM piecesize closest to size */
552 xpm_closest_to (char *dirname, int size, char *ext)
555 int sm_diff = MAXSQSIZE;
559 xpm_getavail(dirname, ext);
561 if (appData.debugMode)
562 xpm_print_avail(stderr, ext);
564 for (i=1; i<MAXSQSIZE; ++i) {
567 diff = (diff<0) ? -diff : diff;
568 if (diff < sm_diff) {
576 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
582 #else /* !HAVE_DIR_STRUCT */
583 /* If we are on a system without a DIR struct, we can't
584 read the directory, so we can't collect a list of
585 filenames, etc., so we can't do any size-fitting. */
587 xpm_closest_to (char *dirname, int size, char *ext)
590 Warning: No DIR structure found on this system --\n\
591 Unable to autosize for XPM/XIM pieces.\n\
592 Please report this error to %s.\n\
593 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
596 #endif /* HAVE_DIR_STRUCT */
599 /* Arrange to catch delete-window events */
600 Atom wm_delete_window;
602 CatchDeleteWindow (Widget w, String procname)
605 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
606 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
607 XtAugmentTranslations(w, XtParseTranslationTable(buf));
614 XtSetArg(args[0], XtNiconic, False);
615 XtSetValues(shellWidget, args, 1);
617 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
620 //---------------------------------------------------------------------------------------------------------
621 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
624 #define CW_USEDEFAULT (1<<31)
625 #define ICS_TEXT_MENU_SIZE 90
626 #define DEBUG_FILE "xboard.debug"
627 #define SetCurrentDirectory chdir
628 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
632 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
635 // front-end part of option handling
637 // [HGM] This platform-dependent table provides the location for storing the color info
638 extern char *crWhite, * crBlack;
642 &appData.whitePieceColor,
643 &appData.blackPieceColor,
644 &appData.lightSquareColor,
645 &appData.darkSquareColor,
646 &appData.highlightSquareColor,
647 &appData.premoveHighlightColor,
648 &appData.lowTimeWarningColor,
659 // [HGM] font: keep a font for each square size, even non-stndard ones
662 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
663 char *fontTable[NUM_FONTS][MAX_SIZE];
666 ParseFont (char *name, int number)
667 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
669 if(sscanf(name, "size%d:", &size)) {
670 // [HGM] font: font is meant for specific boardSize (likely from settings file);
671 // defer processing it until we know if it matches our board size
672 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
673 fontTable[number][size] = strdup(strchr(name, ':')+1);
674 fontValid[number][size] = True;
679 case 0: // CLOCK_FONT
680 appData.clockFont = strdup(name);
682 case 1: // MESSAGE_FONT
683 appData.font = strdup(name);
685 case 2: // COORD_FONT
686 appData.coordFont = strdup(name);
691 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
696 { // only 2 fonts currently
697 appData.clockFont = CLOCK_FONT_NAME;
698 appData.coordFont = COORD_FONT_NAME;
699 appData.font = DEFAULT_FONT_NAME;
704 { // no-op, until we identify the code for this already in XBoard and move it here
708 ParseColor (int n, char *name)
709 { // in XBoard, just copy the color-name string
710 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
714 ParseTextAttribs (ColorClass cc, char *s)
716 (&appData.colorShout)[cc] = strdup(s);
720 ParseBoardSize (void *addr, char *name)
722 appData.boardSize = strdup(name);
727 { // In XBoard the sound-playing program takes care of obtaining the actual sound
731 SetCommPortDefaults ()
732 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
735 // [HGM] args: these three cases taken out to stay in front-end
737 SaveFontArg (FILE *f, ArgDescriptor *ad)
740 int i, n = (int)(intptr_t)ad->argLoc;
742 case 0: // CLOCK_FONT
743 name = appData.clockFont;
745 case 1: // MESSAGE_FONT
748 case 2: // COORD_FONT
749 name = appData.coordFont;
754 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
755 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
756 fontTable[n][squareSize] = strdup(name);
757 fontValid[n][squareSize] = True;
760 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
761 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
766 { // nothing to do, as the sounds are at all times represented by their text-string names already
770 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
771 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
772 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
776 SaveColor (FILE *f, ArgDescriptor *ad)
777 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
778 if(colorVariable[(int)(intptr_t)ad->argLoc])
779 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
783 SaveBoardSize (FILE *f, char *name, void *addr)
784 { // wrapper to shield back-end from BoardSize & sizeInfo
785 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
789 ParseCommPortSettings (char *s)
790 { // no such option in XBoard (yet)
796 GetActualPlacement (Widget wg, WindowPlacement *wp)
798 XWindowAttributes winAt;
805 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
806 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
807 wp->x = rx - winAt.x;
808 wp->y = ry - winAt.y;
809 wp->height = winAt.height;
810 wp->width = winAt.width;
811 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
816 { // wrapper to shield use of window handles from back-end (make addressible by number?)
817 // In XBoard this will have to wait until awareness of window parameters is implemented
818 GetActualPlacement(shellWidget, &wpMain);
819 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
820 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
821 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
822 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
823 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
824 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
828 PrintCommPortSettings (FILE *f, char *name)
829 { // This option does not exist in XBoard
833 EnsureOnScreen (int *x, int *y, int minX, int minY)
840 { // [HGM] args: allows testing if main window is realized from back-end
841 return xBoardWindow != 0;
845 PopUpStartupDialog ()
846 { // start menu not implemented in XBoard
850 ConvertToLine (int argc, char **argv)
852 static char line[128*1024], buf[1024];
856 for(i=1; i<argc; i++)
858 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
859 && argv[i][0] != '{' )
860 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
862 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
863 strncat(line, buf, 128*1024 - strlen(line) - 1 );
866 line[strlen(line)-1] = NULLCHAR;
870 //--------------------------------------------------------------------------------------------
873 ResizeBoardWindow (int w, int h, int inhibit)
875 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
877 shellArgs[0].value = w;
878 shellArgs[1].value = h;
879 shellArgs[4].value = shellArgs[2].value = w;
880 shellArgs[5].value = shellArgs[3].value = h;
881 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
883 XSync(xDisplay, False);
887 MakeOneColor (char *name, Pixel *color)
890 if (!appData.monoMode) {
891 vFrom.addr = (caddr_t) name;
892 vFrom.size = strlen(name);
893 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
894 if (vTo.addr == NULL) {
895 appData.monoMode = True;
898 *color = *(Pixel *) vTo.addr;
906 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
907 int forceMono = False;
909 if (appData.lowTimeWarning)
910 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
911 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
912 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
918 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
919 { // detervtomine what fonts to use, and create them
923 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
924 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
925 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
926 appData.font = fontTable[MESSAGE_FONT][squareSize];
927 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
928 appData.coordFont = fontTable[COORD_FONT][squareSize];
931 appData.font = InsertPxlSize(appData.font, fontPxlSize);
932 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
933 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
934 fontSet = CreateFontSet(appData.font);
935 clockFontSet = CreateFontSet(appData.clockFont);
937 /* For the coordFont, use the 0th font of the fontset. */
938 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
939 XFontStruct **font_struct_list;
940 XFontSetExtents *fontSize;
941 char **font_name_list;
942 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
943 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
944 coordFontStruct = XQueryFont(xDisplay, coordFontID);
945 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
946 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
949 appData.font = FindFont(appData.font, fontPxlSize);
950 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
951 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
952 clockFontID = XLoadFont(xDisplay, appData.clockFont);
953 clockFontStruct = XQueryFont(xDisplay, clockFontID);
954 coordFontID = XLoadFont(xDisplay, appData.coordFont);
955 coordFontStruct = XQueryFont(xDisplay, coordFontID);
956 // textHeight in !NLS mode!
958 countFontID = coordFontID; // [HGM] holdings
959 countFontStruct = coordFontStruct;
961 xdb = XtDatabase(xDisplay);
963 XrmPutLineResource(&xdb, "*international: True");
964 vTo.size = sizeof(XFontSet);
965 vTo.addr = (XtPointer) &fontSet;
966 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
968 XrmPutStringResource(&xdb, "*font", appData.font);
978 case ArgInt: p = " N"; break;
979 case ArgString: p = " STR"; break;
980 case ArgBoolean: p = " TF"; break;
981 case ArgSettingsFilename:
982 case ArgFilename: p = " FILE"; break;
983 case ArgX: p = " Nx"; break;
984 case ArgY: p = " Ny"; break;
985 case ArgAttribs: p = " TEXTCOL"; break;
986 case ArgColor: p = " COL"; break;
987 case ArgFont: p = " FONT"; break;
988 case ArgBoardSize: p = " SIZE"; break;
989 case ArgFloat: p = " FLOAT"; break;
994 case ArgCommSettings:
1005 ArgDescriptor *q, *p = argDescriptors+5;
1006 printf("\nXBoard accepts the following options:\n"
1007 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1008 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1009 " SIZE = board-size spec(s)\n"
1010 " Within parentheses are short forms, or options to set to true or false.\n"
1011 " Persistent options (saved in the settings file) are marked with *)\n\n");
1013 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1014 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1015 if(p->save) strcat(buf+len, "*");
1016 for(q=p+1; q->argLoc == p->argLoc; q++) {
1017 if(q->argName[0] == '-') continue;
1018 strcat(buf+len, q == p+1 ? " (" : " ");
1019 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1021 if(q != p+1) strcat(buf+len, ")");
1023 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1026 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1030 main (int argc, char **argv)
1032 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1033 XSetWindowAttributes window_attributes;
1035 Dimension boardWidth, boardHeight, w, h;
1037 int forceMono = False;
1039 srandom(time(0)); // [HGM] book: make random truly random
1041 setbuf(stdout, NULL);
1042 setbuf(stderr, NULL);
1045 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1046 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1050 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1055 programName = strrchr(argv[0], '/');
1056 if (programName == NULL)
1057 programName = argv[0];
1062 XtSetLanguageProc(NULL, NULL, NULL);
1063 if (appData.debugMode) {
1064 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1067 bindtextdomain(PACKAGE, LOCALEDIR);
1068 textdomain(PACKAGE);
1071 appData.boardSize = "";
1072 InitAppData(ConvertToLine(argc, argv));
1074 if (p == NULL) p = "/tmp";
1075 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1076 gameCopyFilename = (char*) malloc(i);
1077 gamePasteFilename = (char*) malloc(i);
1078 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1079 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1081 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1082 static char buf[MSG_SIZ];
1083 EscapeExpand(buf, appData.firstInitString);
1084 appData.firstInitString = strdup(buf);
1085 EscapeExpand(buf, appData.secondInitString);
1086 appData.secondInitString = strdup(buf);
1087 EscapeExpand(buf, appData.firstComputerString);
1088 appData.firstComputerString = strdup(buf);
1089 EscapeExpand(buf, appData.secondComputerString);
1090 appData.secondComputerString = strdup(buf);
1093 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1096 if (chdir(chessDir) != 0) {
1097 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1103 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1104 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1105 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1106 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1109 setbuf(debugFP, NULL);
1112 /* [HGM,HR] make sure board size is acceptable */
1113 if(appData.NrFiles > BOARD_FILES ||
1114 appData.NrRanks > BOARD_RANKS )
1115 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1118 /* This feature does not work; animation needs a rewrite */
1119 appData.highlightDragging = FALSE;
1123 gameInfo.variant = StringToVariant(appData.variant);
1124 InitPosition(FALSE);
1127 XtAppInitialize(&appContext, "XBoard", shellOptions,
1128 XtNumber(shellOptions),
1129 &argc, argv, xboardResources, NULL, 0);
1131 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1132 clientResources, XtNumber(clientResources),
1135 xDisplay = XtDisplay(shellWidget);
1136 xScreen = DefaultScreen(xDisplay);
1137 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1140 * determine size, based on supplied or remembered -size, or screen size
1142 if (isdigit(appData.boardSize[0])) {
1143 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1144 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1145 &fontPxlSize, &smallLayout, &tinyLayout);
1147 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1148 programName, appData.boardSize);
1152 /* Find some defaults; use the nearest known size */
1153 SizeDefaults *szd, *nearest;
1154 int distance = 99999;
1155 nearest = szd = sizeDefaults;
1156 while (szd->name != NULL) {
1157 if (abs(szd->squareSize - squareSize) < distance) {
1159 distance = abs(szd->squareSize - squareSize);
1160 if (distance == 0) break;
1164 if (i < 2) lineGap = nearest->lineGap;
1165 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1166 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1167 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1168 if (i < 6) smallLayout = nearest->smallLayout;
1169 if (i < 7) tinyLayout = nearest->tinyLayout;
1172 SizeDefaults *szd = sizeDefaults;
1173 if (*appData.boardSize == NULLCHAR) {
1174 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1175 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1178 if (szd->name == NULL) szd--;
1179 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1181 while (szd->name != NULL &&
1182 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1183 if (szd->name == NULL) {
1184 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1185 programName, appData.boardSize);
1189 squareSize = szd->squareSize;
1190 lineGap = szd->lineGap;
1191 clockFontPxlSize = szd->clockFontPxlSize;
1192 coordFontPxlSize = szd->coordFontPxlSize;
1193 fontPxlSize = szd->fontPxlSize;
1194 smallLayout = szd->smallLayout;
1195 tinyLayout = szd->tinyLayout;
1196 // [HGM] font: use defaults from settings file if available and not overruled
1199 defaultLineGap = lineGap;
1200 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1202 /* [HR] height treated separately (hacked) */
1203 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1204 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1207 * Determine what fonts to use.
1209 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1212 * Detect if there are not enough colors available and adapt.
1214 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1215 appData.monoMode = True;
1218 forceMono = MakeColors();
1221 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1223 appData.monoMode = True;
1226 if (appData.monoMode && appData.debugMode) {
1227 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1228 (unsigned long) XWhitePixel(xDisplay, xScreen),
1229 (unsigned long) XBlackPixel(xDisplay, xScreen));
1232 ParseIcsTextColors();
1234 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1240 layoutName = "tinyLayout";
1241 } else if (smallLayout) {
1242 layoutName = "smallLayout";
1244 layoutName = "normalLayout";
1247 optList = BoardPopUp(squareSize, lineGap, (void*)
1253 InitDrawingHandle(optList + W_BOARD);
1254 currBoard = &optList[W_BOARD];
1255 boardWidget = optList[W_BOARD].handle;
1256 menuBarWidget = optList[W_MENU].handle;
1257 dropMenu = optList[W_DROP].handle;
1258 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1259 formWidget = XtParent(boardWidget);
1260 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1261 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1262 XtGetValues(optList[W_WHITE].handle, args, 2);
1263 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1264 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1265 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1266 XtGetValues(optList[W_PAUSE].handle, args, 2);
1268 AppendEnginesToMenu(appData.recentEngineList);
1270 xBoardWindow = XtWindow(boardWidget);
1272 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1273 // not need to go into InitDrawingSizes().
1276 * Create X checkmark bitmap and initialize option menu checks.
1278 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1279 checkmark_bits, checkmark_width, checkmark_height);
1285 ReadBitmap(&wIconPixmap, "icon_white.bm",
1286 icon_white_bits, icon_white_width, icon_white_height);
1287 ReadBitmap(&bIconPixmap, "icon_black.bm",
1288 icon_black_bits, icon_black_width, icon_black_height);
1289 iconPixmap = wIconPixmap;
1291 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1292 XtSetValues(shellWidget, args, i);
1295 * Create a cursor for the board widget.
1297 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1298 XChangeWindowAttributes(xDisplay, xBoardWindow,
1299 CWCursor, &window_attributes);
1302 * Inhibit shell resizing.
1304 shellArgs[0].value = (XtArgVal) &w;
1305 shellArgs[1].value = (XtArgVal) &h;
1306 XtGetValues(shellWidget, shellArgs, 2);
1307 shellArgs[4].value = shellArgs[2].value = w;
1308 shellArgs[5].value = shellArgs[3].value = h;
1309 // XtSetValues(shellWidget, &shellArgs[2], 4);
1310 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1311 marginH = h - boardHeight;
1313 CatchDeleteWindow(shellWidget, "QuitProc");
1318 if(appData.logoSize)
1319 { // locate and read user logo
1321 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1322 ASSIGN(userLogo, buf);
1325 if (appData.animate || appData.animateDragging)
1328 XtAugmentTranslations(formWidget,
1329 XtParseTranslationTable(globalTranslations));
1331 XtAddEventHandler(formWidget, KeyPressMask, False,
1332 (XtEventHandler) MoveTypeInProc, NULL);
1333 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1334 (XtEventHandler) EventProc, NULL);
1336 /* [AS] Restore layout */
1337 if( wpMoveHistory.visible ) {
1341 if( wpEvalGraph.visible )
1346 if( wpEngineOutput.visible ) {
1347 EngineOutputPopUp();
1352 if (errorExitStatus == -1) {
1353 if (appData.icsActive) {
1354 /* We now wait until we see "login:" from the ICS before
1355 sending the logon script (problems with timestamp otherwise) */
1356 /*ICSInitScript();*/
1357 if (appData.icsInputBox) ICSInputBoxPopUp();
1361 signal(SIGWINCH, TermSizeSigHandler);
1363 signal(SIGINT, IntSigHandler);
1364 signal(SIGTERM, IntSigHandler);
1365 if (*appData.cmailGameName != NULLCHAR) {
1366 signal(SIGUSR1, CmailSigHandler);
1370 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1373 // XtSetKeyboardFocus(shellWidget, formWidget);
1374 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1376 XtAppMainLoop(appContext);
1377 if (appData.debugMode) fclose(debugFP); // [DM] debug
1382 TermSizeSigHandler (int sig)
1388 IntSigHandler (int sig)
1394 CmailSigHandler (int sig)
1399 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1401 /* Activate call-back function CmailSigHandlerCallBack() */
1402 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1404 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1408 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1411 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1413 /**** end signal code ****/
1416 #define Abs(n) ((n)<0 ? -(n) : (n))
1420 InsertPxlSize (char *pattern, int targetPxlSize)
1422 char *base_fnt_lst, strInt[12], *p, *q;
1423 int alternatives, i, len, strIntLen;
1426 * Replace the "*" (if present) in the pixel-size slot of each
1427 * alternative with the targetPxlSize.
1431 while ((p = strchr(p, ',')) != NULL) {
1435 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1436 strIntLen = strlen(strInt);
1437 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1441 while (alternatives--) {
1442 char *comma = strchr(p, ',');
1443 for (i=0; i<14; i++) {
1444 char *hyphen = strchr(p, '-');
1446 if (comma && hyphen > comma) break;
1447 len = hyphen + 1 - p;
1448 if (i == 7 && *p == '*' && len == 2) {
1450 memcpy(q, strInt, strIntLen);
1460 len = comma + 1 - p;
1467 return base_fnt_lst;
1471 CreateFontSet (char *base_fnt_lst)
1474 char **missing_list;
1478 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1479 &missing_list, &missing_count, &def_string);
1480 if (appData.debugMode) {
1482 XFontStruct **font_struct_list;
1483 char **font_name_list;
1484 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1486 fprintf(debugFP, " got list %s, locale %s\n",
1487 XBaseFontNameListOfFontSet(fntSet),
1488 XLocaleOfFontSet(fntSet));
1489 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1490 for (i = 0; i < count; i++) {
1491 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1494 for (i = 0; i < missing_count; i++) {
1495 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1498 if (fntSet == NULL) {
1499 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1504 #else // not ENABLE_NLS
1506 * Find a font that matches "pattern" that is as close as
1507 * possible to the targetPxlSize. Prefer fonts that are k
1508 * pixels smaller to fonts that are k pixels larger. The
1509 * pattern must be in the X Consortium standard format,
1510 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1511 * The return value should be freed with XtFree when no
1515 FindFont (char *pattern, int targetPxlSize)
1517 char **fonts, *p, *best, *scalable, *scalableTail;
1518 int i, j, nfonts, minerr, err, pxlSize;
1520 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1522 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1523 programName, pattern);
1530 for (i=0; i<nfonts; i++) {
1533 if (*p != '-') continue;
1535 if (*p == NULLCHAR) break;
1536 if (*p++ == '-') j++;
1538 if (j < 7) continue;
1541 scalable = fonts[i];
1544 err = pxlSize - targetPxlSize;
1545 if (Abs(err) < Abs(minerr) ||
1546 (minerr > 0 && err < 0 && -err == minerr)) {
1552 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1553 /* If the error is too big and there is a scalable font,
1554 use the scalable font. */
1555 int headlen = scalableTail - scalable;
1556 p = (char *) XtMalloc(strlen(scalable) + 10);
1557 while (isdigit(*scalableTail)) scalableTail++;
1558 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1560 p = (char *) XtMalloc(strlen(best) + 2);
1561 safeStrCpy(p, best, strlen(best)+1 );
1563 if (appData.debugMode) {
1564 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1565 pattern, targetPxlSize, p);
1567 XFreeFontNames(fonts);
1573 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1576 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1582 MarkMenuItem (char *menuRef, int state)
1584 MenuItem *item = MenuNameToItem(menuRef);
1588 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1589 XtSetValues(item->handle, args, 1);
1594 EnableNamedMenuItem (char *menuRef, int state)
1596 MenuItem *item = MenuNameToItem(menuRef);
1598 if(item) XtSetSensitive(item->handle, state);
1602 EnableButtonBar (int state)
1604 XtSetSensitive(optList[W_BUTTON].handle, state);
1609 SetMenuEnables (Enables *enab)
1611 while (enab->name != NULL) {
1612 EnableNamedMenuItem(enab->name, enab->value);
1618 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1619 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1621 if(*nprms == 0) return;
1622 item = MenuNameToItem(prms[0]);
1623 if(item) ((MenuProc *) item->proc) ();
1627 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
1629 RecentEngineEvent((int) (intptr_t) addr);
1633 AppendMenuItem (char *msg, int n)
1635 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
1647 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1648 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1649 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1650 dmEnables[i].piece);
1651 XtSetSensitive(entry, p != NULL || !appData.testLegality
1652 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1653 && !appData.icsActive));
1655 while (p && *p++ == dmEnables[i].piece) count++;
1656 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1658 XtSetArg(args[j], XtNlabel, label); j++;
1659 XtSetValues(entry, args, j);
1664 do_flash_delay (unsigned long msec)
1670 FlashDelay (int flash_delay)
1672 XSync(xDisplay, False);
1673 if(flash_delay) do_flash_delay(flash_delay);
1677 Fraction (int x, int start, int stop)
1679 double f = ((double) x - start)/(stop - start);
1680 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1684 static WindowPlacement wpNew;
1687 CoDrag (Widget sh, WindowPlacement *wp)
1690 int j=0, touch=0, fudge = 2;
1691 GetActualPlacement(sh, wp);
1692 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1693 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1694 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1695 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1696 if(!touch ) return; // only windows that touch co-move
1697 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1698 int heightInc = wpNew.height - wpMain.height;
1699 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1700 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1701 wp->y += fracTop * heightInc;
1702 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1703 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1704 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1705 int widthInc = wpNew.width - wpMain.width;
1706 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1707 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1708 wp->y += fracLeft * widthInc;
1709 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1710 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1712 wp->x += wpNew.x - wpMain.x;
1713 wp->y += wpNew.y - wpMain.y;
1714 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1715 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1716 XtSetArg(args[j], XtNx, wp->x); j++;
1717 XtSetArg(args[j], XtNy, wp->y); j++;
1718 XtSetValues(sh, args, j);
1722 ReSize (WindowPlacement *wp)
1725 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1726 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1727 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1728 if(sqy < sqx) sqx = sqy;
1729 if(sqx != squareSize) {
1730 squareSize = sqx; // adopt new square size
1731 CreatePNGPieces(); // make newly scaled pieces
1732 InitDrawingSizes(0, 0); // creates grid etc.
1733 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1734 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1735 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1736 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1737 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1740 static XtIntervalId delayedDragID = 0;
1749 GetActualPlacement(shellWidget, &wpNew);
1750 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1751 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1752 busy = 0; return; // false alarm
1755 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1756 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1757 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1758 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1760 DrawPosition(True, NULL);
1761 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1769 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1771 XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1775 EventProc (Widget widget, caddr_t unused, XEvent *event)
1777 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1778 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1782 * event handler for redrawing the board
1785 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1787 DrawPosition(True, NULL);
1792 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1793 { // [HGM] pv: walk PV
1794 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1797 static int savedIndex; /* gross that this is global */
1800 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1803 XawTextPosition index, dummy;
1806 XawTextGetSelectionPos(w, &index, &dummy);
1807 XtSetArg(arg, XtNstring, &val);
1808 XtGetValues(w, &arg, 1);
1809 ReplaceComment(savedIndex, val);
1810 if(savedIndex != currentMove) ToNrEvent(savedIndex);
1811 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1815 EditCommentPopUp (int index, char *title, char *text)
1818 if (text == NULL) text = "";
1819 NewCommentPopup(title, text, index);
1823 CommentPopUp (char *title, char *text)
1825 savedIndex = currentMove; // [HGM] vari
1826 NewCommentPopup(title, text, currentMove);
1832 PopDown(CommentDlg);
1836 /* Disable all user input other than deleting the window */
1837 static int frozen = 0;
1843 /* Grab by a widget that doesn't accept input */
1844 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1848 /* Undo a FreezeUI */
1852 if (!frozen) return;
1853 XtRemoveGrab(optList[W_MESSG].handle);
1861 static int oldPausing = FALSE;
1862 static GameMode oldmode = (GameMode) -1;
1865 if (!boardWidget || !XtIsRealized(boardWidget)) return;
1867 if (pausing != oldPausing) {
1868 oldPausing = pausing;
1869 MarkMenuItem("Mode.Pause", pausing);
1871 if (appData.showButtonBar) {
1872 /* Always toggle, don't set. Previous code messes up when
1873 invoked while the button is pressed, as releasing it
1874 toggles the state again. */
1877 XtSetArg(args[0], XtNbackground, &oldbg);
1878 XtSetArg(args[1], XtNforeground, &oldfg);
1879 XtGetValues(optList[W_PAUSE].handle,
1881 XtSetArg(args[0], XtNbackground, oldfg);
1882 XtSetArg(args[1], XtNforeground, oldbg);
1884 XtSetValues(optList[W_PAUSE].handle, args, 2);
1888 wname = ModeToWidgetName(oldmode);
1889 if (wname != NULL) {
1890 MarkMenuItem(wname, False);
1892 wname = ModeToWidgetName(gameMode);
1893 if (wname != NULL) {
1894 MarkMenuItem(wname, True);
1897 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1899 /* Maybe all the enables should be handled here, not just this one */
1900 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1902 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1907 * Button/menu procedures
1910 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1911 char *selected_fen_position=NULL;
1914 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1915 Atom *type_return, XtPointer *value_return,
1916 unsigned long *length_return, int *format_return)
1918 char *selection_tmp;
1920 // if (!selected_fen_position) return False; /* should never happen */
1921 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1922 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1923 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1926 if (f == NULL) return False;
1930 selection_tmp = XtMalloc(len + 1);
1931 count = fread(selection_tmp, 1, len, f);
1934 XtFree(selection_tmp);
1937 selection_tmp[len] = NULLCHAR;
1939 /* note: since no XtSelectionDoneProc was registered, Xt will
1940 * automatically call XtFree on the value returned. So have to
1941 * make a copy of it allocated with XtMalloc */
1942 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1943 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1946 *value_return=selection_tmp;
1947 *length_return=strlen(selection_tmp);
1948 *type_return=*target;
1949 *format_return = 8; /* bits per byte */
1951 } else if (*target == XA_TARGETS(xDisplay)) {
1952 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1953 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1954 targets_tmp[1] = XA_STRING;
1955 *value_return = targets_tmp;
1956 *type_return = XA_ATOM;
1959 // This code leads to a read of value_return out of bounds on 64-bit systems.
1960 // Other code which I have seen always sets *format_return to 32 independent of
1961 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1962 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1963 *format_return = 8 * sizeof(Atom);
1964 if (*format_return > 32) {
1965 *length_return *= *format_return / 32;
1966 *format_return = 32;
1969 *format_return = 32;
1977 /* note: when called from menu all parameters are NULL, so no clue what the
1978 * Widget which was clicked on was, or what the click event was
1981 CopySomething (char *src)
1983 selected_fen_position = src;
1985 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
1986 * have a notion of a position that is selected but not copied.
1987 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
1989 XtOwnSelection(menuBarWidget, XA_PRIMARY,
1991 SendPositionSelection,
1992 NULL/* lose_ownership_proc */ ,
1993 NULL/* transfer_done_proc */);
1994 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
1996 SendPositionSelection,
1997 NULL/* lose_ownership_proc */ ,
1998 NULL/* transfer_done_proc */);
2001 /* function called when the data to Paste is ready */
2003 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2004 Atom *type, XtPointer value, unsigned long *len, int *format)
2007 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2008 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2009 EditPositionPasteFEN(fenstr);
2013 /* called when Paste Position button is pressed,
2014 * all parameters will be NULL */
2016 PastePositionProc ()
2018 XtGetSelectionValue(menuBarWidget,
2019 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2020 /* (XtSelectionCallbackProc) */ PastePositionCB,
2021 NULL, /* client_data passed to PastePositionCB */
2023 /* better to use the time field from the event that triggered the
2024 * call to this function, but that isn't trivial to get
2031 /* note: when called from menu all parameters are NULL, so no clue what the
2032 * Widget which was clicked on was, or what the click event was
2034 /* function called when the data to Paste is ready */
2036 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2037 Atom *type, XtPointer value, unsigned long *len, int *format)
2040 if (value == NULL || *len == 0) {
2041 return; /* nothing had been selected to copy */
2043 f = fopen(gamePasteFilename, "w");
2045 DisplayError(_("Can't open temp file"), errno);
2048 fwrite(value, 1, *len, f);
2051 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2054 /* called when Paste Game button is pressed,
2055 * all parameters will be NULL */
2059 XtGetSelectionValue(menuBarWidget,
2060 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2061 /* (XtSelectionCallbackProc) */ PasteGameCB,
2062 NULL, /* client_data passed to PasteGameCB */
2064 /* better to use the time field from the event that triggered the
2065 * call to this function, but that isn't trivial to get
2074 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2081 { // bassic primitive for determining if modifier keys are pressed
2082 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2085 XQueryKeymap(xDisplay,keys);
2086 for(i=0; i<6; i++) {
2088 j = XKeysymToKeycode(xDisplay, codes[i]);
2089 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2095 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2099 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2100 if ( n == 1 && *buf >= 32 // printable
2101 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2102 ) BoxAutoPopUp (buf);
2106 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2107 { // [HGM] input: let up-arrow recall previous line from history
2112 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2113 { // [HGM] input: let down-arrow recall next line from history
2118 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2124 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2126 if (!TempBackwardActive) {
2127 TempBackwardActive = True;
2133 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2135 /* Check to see if triggered by a key release event for a repeating key.
2136 * If so the next queued event will be a key press of the same key at the same time */
2137 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2139 XPeekEvent(xDisplay, &next);
2140 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2141 next.xkey.keycode == event->xkey.keycode)
2145 TempBackwardActive = False;
2149 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2150 { // called as key binding
2153 if (nprms && *nprms > 0)
2157 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2163 { // called from menu
2164 ManInner(NULL, NULL, NULL, NULL);
2168 SetWindowTitle (char *text, char *title, char *icon)
2172 if (appData.titleInWindow) {
2174 XtSetArg(args[i], XtNlabel, text); i++;
2175 XtSetValues(titleWidget, args, i);
2178 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2179 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2180 XtSetValues(shellWidget, args, i);
2181 XSync(xDisplay, False);
2186 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2192 DisplayIcsInteractionTitle (String message)
2194 if (oldICSInteractionTitle == NULL) {
2195 /* Magic to find the old window title, adapted from vim */
2196 char *wina = getenv("WINDOWID");
2198 Window win = (Window) atoi(wina);
2199 Window root, parent, *children;
2200 unsigned int nchildren;
2201 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2203 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2204 if (!XQueryTree(xDisplay, win, &root, &parent,
2205 &children, &nchildren)) break;
2206 if (children) XFree((void *)children);
2207 if (parent == root || parent == 0) break;
2210 XSetErrorHandler(oldHandler);
2212 if (oldICSInteractionTitle == NULL) {
2213 oldICSInteractionTitle = "xterm";
2216 printf("\033]0;%s\007", message);
2221 XtIntervalId delayedEventTimerXID = 0;
2222 DelayedEventCallback delayedEventCallback = 0;
2227 delayedEventTimerXID = 0;
2228 delayedEventCallback();
2232 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2234 if(delayedEventTimerXID && delayedEventCallback == cb)
2235 // [HGM] alive: replace, rather than add or flush identical event
2236 XtRemoveTimeOut(delayedEventTimerXID);
2237 delayedEventCallback = cb;
2238 delayedEventTimerXID =
2239 XtAppAddTimeOut(appContext, millisec,
2240 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2243 DelayedEventCallback
2246 if (delayedEventTimerXID) {
2247 return delayedEventCallback;
2254 CancelDelayedEvent ()
2256 if (delayedEventTimerXID) {
2257 XtRemoveTimeOut(delayedEventTimerXID);
2258 delayedEventTimerXID = 0;
2262 XtIntervalId loadGameTimerXID = 0;
2265 LoadGameTimerRunning ()
2267 return loadGameTimerXID != 0;
2271 StopLoadGameTimer ()
2273 if (loadGameTimerXID != 0) {
2274 XtRemoveTimeOut(loadGameTimerXID);
2275 loadGameTimerXID = 0;
2283 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2285 loadGameTimerXID = 0;
2290 StartLoadGameTimer (long millisec)
2293 XtAppAddTimeOut(appContext, millisec,
2294 (XtTimerCallbackProc) LoadGameTimerCallback,
2298 XtIntervalId analysisClockXID = 0;
2301 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2303 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2304 || appData.icsEngineAnalyze) { // [DM]
2305 AnalysisPeriodicEvent(0);
2306 StartAnalysisClock();
2311 StartAnalysisClock ()
2314 XtAppAddTimeOut(appContext, 2000,
2315 (XtTimerCallbackProc) AnalysisClockCallback,
2319 XtIntervalId clockTimerXID = 0;
2322 ClockTimerRunning ()
2324 return clockTimerXID != 0;
2330 if (clockTimerXID != 0) {
2331 XtRemoveTimeOut(clockTimerXID);
2340 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2347 StartClockTimer (long millisec)
2350 XtAppAddTimeOut(appContext, millisec,
2351 (XtTimerCallbackProc) ClockTimerCallback,
2356 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2360 Widget w = (Widget) opt->handle;
2362 /* check for low time warning */
2363 Pixel foregroundOrWarningColor = timerForegroundPixel;
2366 appData.lowTimeWarning &&
2367 (timer / 1000) < appData.icsAlarmTime)
2368 foregroundOrWarningColor = lowTimeWarningColor;
2370 if (appData.clockMode) {
2371 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2372 XtSetArg(args[0], XtNlabel, buf);
2374 snprintf(buf, MSG_SIZ, "%s ", color);
2375 XtSetArg(args[0], XtNlabel, buf);
2380 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2381 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2383 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2384 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2387 XtSetValues(w, args, 3);
2390 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2393 SetClockIcon (int color)
2396 Pixmap pm = *clockIcons[color];
2397 if (iconPixmap != pm) {
2399 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2400 XtSetValues(shellWidget, args, 1);
2404 #define INPUT_SOURCE_BUF_SIZE 8192
2413 char buf[INPUT_SOURCE_BUF_SIZE];
2418 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2420 InputSource *is = (InputSource *) closure;
2425 if (is->lineByLine) {
2426 count = read(is->fd, is->unused,
2427 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2429 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2432 is->unused += count;
2434 while (p < is->unused) {
2435 q = memchr(p, '\n', is->unused - p);
2436 if (q == NULL) break;
2438 (is->func)(is, is->closure, p, q - p, 0);
2442 while (p < is->unused) {
2447 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2452 (is->func)(is, is->closure, is->buf, count, error);
2457 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2460 ChildProc *cp = (ChildProc *) pr;
2462 is = (InputSource *) calloc(1, sizeof(InputSource));
2463 is->lineByLine = lineByLine;
2467 is->fd = fileno(stdin);
2469 is->kind = cp->kind;
2470 is->fd = cp->fdFrom;
2473 is->unused = is->buf;
2476 is->xid = XtAppAddInput(appContext, is->fd,
2477 (XtPointer) (XtInputReadMask),
2478 (XtInputCallbackProc) DoInputCallback,
2480 is->closure = closure;
2481 return (InputSourceRef) is;
2485 RemoveInputSource (InputSourceRef isr)
2487 InputSource *is = (InputSource *) isr;
2489 if (is->xid == 0) return;
2490 XtRemoveInput(is->xid);
2496 static Boolean frameWaiting;
2499 FrameAlarm (int sig)
2501 frameWaiting = False;
2502 /* In case System-V style signals. Needed?? */
2503 signal(SIGALRM, FrameAlarm);
2507 FrameDelay (int time)
2509 struct itimerval delay;
2511 XSync(xDisplay, False);
2514 frameWaiting = True;
2515 signal(SIGALRM, FrameAlarm);
2516 delay.it_interval.tv_sec =
2517 delay.it_value.tv_sec = time / 1000;
2518 delay.it_interval.tv_usec =
2519 delay.it_value.tv_usec = (time % 1000) * 1000;
2520 setitimer(ITIMER_REAL, &delay, NULL);
2521 while (frameWaiting) pause();
2522 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2523 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2524 setitimer(ITIMER_REAL, &delay, NULL);
2531 FrameDelay (int time)
2533 XSync(xDisplay, False);
2535 usleep(time * 1000);
2541 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2543 char buf[MSG_SIZ], *logoName = buf;
2544 if(appData.logo[n][0]) {
2545 logoName = appData.logo[n];
2546 } else if(appData.autoLogo) {
2547 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2548 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2549 } else if(appData.directory[n] && appData.directory[n][0]) {
2550 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2554 { ASSIGN(cps->programLogo, logoName); }
2558 UpdateLogos (int displ)
2560 if(optList[W_WHITE-1].handle == NULL) return;
2561 LoadLogo(&first, 0, 0);
2562 LoadLogo(&second, 1, appData.icsActive);
2563 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);