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"
205 #include "xevalgraph.h"
206 #include "xedittags.h"
210 #include "engineoutput.h"
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
231 int main P((int argc, char **argv));
232 RETSIGTYPE CmailSigHandler P((int sig));
233 RETSIGTYPE IntSigHandler P((int sig));
234 RETSIGTYPE TermSizeSigHandler P((int sig));
235 Widget CreateMenuBar P((Menu *mb, int boardWidth));
237 char *InsertPxlSize P((char *pattern, int targetPxlSize));
238 XFontSet CreateFontSet P((char *base_fnt_lst));
240 char *FindFont P((char *pattern, int targetPxlSize));
242 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
243 u_int wreq, u_int hreq));
244 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
245 void DelayedDrag P((void));
246 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
247 void HandlePV P((Widget w, XEvent * event,
248 String * params, Cardinal * nParams));
249 void DrawPositionProc P((Widget w, XEvent *event,
250 String *prms, Cardinal *nprms));
251 void CommentClick P((Widget w, XEvent * event,
252 String * params, Cardinal * nParams));
253 void ICSInputBoxPopUp P((void));
254 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
255 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
256 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
257 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
258 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
259 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
260 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
261 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
262 Boolean TempBackwardActive = False;
263 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
264 void DisplayMove P((int moveNumber));
265 void ICSInitScript P((void));
266 void update_ics_width P(());
267 int CopyMemoProc P(());
270 * XBoard depends on Xt R4 or higher
272 int xtVersion = XtSpecificationRelease;
277 Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
278 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
279 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
280 Option *optList; // contains all widgets of main window
282 XFontSet fontSet, clockFontSet;
285 XFontStruct *clockFontStruct;
287 Font coordFontID, countFontID;
288 XFontStruct *coordFontStruct, *countFontStruct;
289 XtAppContext appContext;
292 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
294 Position commentX = -1, commentY = -1;
295 Dimension commentW, commentH;
296 typedef unsigned int BoardSize;
298 Boolean chessProgram;
300 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
301 int smallLayout = 0, tinyLayout = 0,
302 marginW, marginH, // [HGM] for run-time resizing
303 fromX = -1, fromY = -1, toX, toY, commentUp = False,
304 errorExitStatus = -1, defaultLineGap;
305 Dimension textHeight;
306 Pixel timerForegroundPixel, timerBackgroundPixel;
307 Pixel buttonForegroundPixel, buttonBackgroundPixel;
308 char *chessDir, *programName, *programVersion;
309 Boolean alwaysOnTop = False;
310 char *icsTextMenuString;
312 char *firstChessProgramNames;
313 char *secondChessProgramNames;
315 WindowPlacement wpMain;
316 WindowPlacement wpConsole;
317 WindowPlacement wpComment;
318 WindowPlacement wpMoveHistory;
319 WindowPlacement wpEvalGraph;
320 WindowPlacement wpEngineOutput;
321 WindowPlacement wpGameList;
322 WindowPlacement wpTags;
325 /* This magic number is the number of intermediate frames used
326 in each half of the animation. For short moves it's reduced
327 by 1. The total number of frames will be factor * 2 + 1. */
330 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
337 DropMenuEnables dmEnables[] = {
354 XtResource clientResources[] = {
355 { "flashCount", "flashCount", XtRInt, sizeof(int),
356 XtOffset(AppDataPtr, flashCount), XtRImmediate,
357 (XtPointer) FLASH_COUNT },
360 XrmOptionDescRec shellOptions[] = {
361 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
362 { "-flash", "flashCount", XrmoptionNoArg, "3" },
363 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
366 XtActionsRec boardActions[] = {
367 { "DrawPosition", DrawPositionProc },
368 { "HandlePV", HandlePV },
369 { "SelectPV", SelectPV },
370 { "StopPV", StopPV },
371 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
372 { "QuitProc", QuitWrapper },
373 { "ManProc", ManInner },
374 { "TempBackwardProc", TempBackwardProc },
375 { "TempForwardProc", TempForwardProc },
376 { "CommentClick", (XtActionProc) CommentClick },
377 { "GenericPopDown", (XtActionProc) GenericPopDown },
378 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
379 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
380 { "SelectMove", (XtActionProc) SelectMoveX },
381 { "LoadSelectedProc", LoadSelectedProc },
382 { "SetFilterProc", SetFilterProc },
383 { "TypeInProc", TypeInProc },
384 { "EnterKeyProc", EnterKeyProc },
385 { "UpKeyProc", UpKeyProc },
386 { "DownKeyProc", DownKeyProc },
387 { "WheelProc", WheelProc },
388 { "TabProc", TabProc },
391 char globalTranslations[] =
392 ":<Key>F9: MenuItem(Actions.Resign) \n \
393 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
394 :Meta<Key>V: MenuItem(File.NewVariant) \n \
395 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
396 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
397 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
398 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
399 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
400 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
401 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
402 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
403 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
404 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
405 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
406 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
407 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
408 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
409 :Ctrl<Key>q: MenuItem(File.Quit) \n \
410 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
411 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
412 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
413 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
414 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
415 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
416 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
417 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
418 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
419 :Meta<Key>G: MenuItem(View.GameList) \n \
420 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
421 :<Key>Pause: MenuItem(Mode.Pause) \n \
422 :<Key>F3: MenuItem(Action.Accept) \n \
423 :<Key>F4: MenuItem(Action.Decline) \n \
424 :<Key>F12: MenuItem(Action.Rematch) \n \
425 :<Key>F5: MenuItem(Action.CallFlag) \n \
426 :<Key>F6: MenuItem(Action.Draw) \n \
427 :<Key>F7: MenuItem(Action.Adjourn) \n \
428 :<Key>F8: MenuItem(Action.Abort) \n \
429 :<Key>F10: MenuItem(Action.StopObserving) \n \
430 :<Key>F11: MenuItem(Action.StopExamining) \n \
431 :Ctrl<Key>d: MenuItem(DebugProc) \n \
432 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
433 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
434 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
435 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
436 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
437 :<Key>Left: MenuItem(Edit.Backward) \n \
438 :<Key>Right: MenuItem(Edit.Forward) \n \
439 :<Key>Home: MenuItem(Edit.Revert) \n \
440 :<Key>End: MenuItem(Edit.TruncateGame) \n \
441 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
442 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
443 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
444 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
445 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
446 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
447 #ifndef OPTIONSDIALOG
449 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
450 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
451 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
452 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
453 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
456 :<Key>F1: MenuItem(Help.ManXBoard) \n \
457 :<Key>F2: MenuItem(View.FlipView) \n \
458 :<KeyDown>Return: TempBackwardProc() \n \
459 :<KeyUp>Return: TempForwardProc() \n";
461 char ICSInputTranslations[] =
462 "<Key>Up: UpKeyProc() \n "
463 "<Key>Down: DownKeyProc() \n "
464 "<Key>Return: EnterKeyProc() \n";
466 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
467 // as the widget is destroyed before the up-click can call extend-end
468 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
470 String xboardResources[] = {
471 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
476 /* Max possible square size */
477 #define MAXSQSIZE 256
479 static int xpm_avail[MAXSQSIZE];
481 #ifdef HAVE_DIR_STRUCT
483 /* Extract piece size from filename */
485 xpm_getsize (char *name, int len, char *ext)
493 if ((p=strchr(name, '.')) == NULL ||
494 StrCaseCmp(p+1, ext) != 0)
500 while (*p && isdigit(*p))
507 /* Setup xpm_avail */
509 xpm_getavail (char *dirname, char *ext)
515 for (i=0; i<MAXSQSIZE; ++i)
518 if (appData.debugMode)
519 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
521 dir = opendir(dirname);
524 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
525 programName, dirname);
529 while ((ent=readdir(dir)) != NULL) {
530 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
531 if (i > 0 && i < MAXSQSIZE)
541 xpm_print_avail (FILE *fp, char *ext)
545 fprintf(fp, _("Available `%s' sizes:\n"), ext);
546 for (i=1; i<MAXSQSIZE; ++i) {
552 /* Return XPM piecesize closest to size */
554 xpm_closest_to (char *dirname, int size, char *ext)
557 int sm_diff = MAXSQSIZE;
561 xpm_getavail(dirname, ext);
563 if (appData.debugMode)
564 xpm_print_avail(stderr, ext);
566 for (i=1; i<MAXSQSIZE; ++i) {
569 diff = (diff<0) ? -diff : diff;
570 if (diff < sm_diff) {
578 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
584 #else /* !HAVE_DIR_STRUCT */
585 /* If we are on a system without a DIR struct, we can't
586 read the directory, so we can't collect a list of
587 filenames, etc., so we can't do any size-fitting. */
589 xpm_closest_to (char *dirname, int size, char *ext)
592 Warning: No DIR structure found on this system --\n\
593 Unable to autosize for XPM/XIM pieces.\n\
594 Please report this error to %s.\n\
595 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
598 #endif /* HAVE_DIR_STRUCT */
601 /* Arrange to catch delete-window events */
602 Atom wm_delete_window;
604 CatchDeleteWindow (Widget w, String procname)
607 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
608 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
609 XtAugmentTranslations(w, XtParseTranslationTable(buf));
616 XtSetArg(args[0], XtNiconic, False);
617 XtSetValues(shellWidget, args, 1);
619 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
622 //---------------------------------------------------------------------------------------------------------
623 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
626 #define CW_USEDEFAULT (1<<31)
627 #define ICS_TEXT_MENU_SIZE 90
628 #define DEBUG_FILE "xboard.debug"
629 #define SetCurrentDirectory chdir
630 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
634 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
637 // front-end part of option handling
639 // [HGM] This platform-dependent table provides the location for storing the color info
640 extern char *crWhite, * crBlack;
644 &appData.whitePieceColor,
645 &appData.blackPieceColor,
646 &appData.lightSquareColor,
647 &appData.darkSquareColor,
648 &appData.highlightSquareColor,
649 &appData.premoveHighlightColor,
650 &appData.lowTimeWarningColor,
661 // [HGM] font: keep a font for each square size, even non-stndard ones
664 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
665 char *fontTable[NUM_FONTS][MAX_SIZE];
668 ParseFont (char *name, int number)
669 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
671 if(sscanf(name, "size%d:", &size)) {
672 // [HGM] font: font is meant for specific boardSize (likely from settings file);
673 // defer processing it until we know if it matches our board size
674 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
675 fontTable[number][size] = strdup(strchr(name, ':')+1);
676 fontValid[number][size] = True;
681 case 0: // CLOCK_FONT
682 appData.clockFont = strdup(name);
684 case 1: // MESSAGE_FONT
685 appData.font = strdup(name);
687 case 2: // COORD_FONT
688 appData.coordFont = strdup(name);
693 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
698 { // only 2 fonts currently
699 appData.clockFont = CLOCK_FONT_NAME;
700 appData.coordFont = COORD_FONT_NAME;
701 appData.font = DEFAULT_FONT_NAME;
706 { // no-op, until we identify the code for this already in XBoard and move it here
710 ParseColor (int n, char *name)
711 { // in XBoard, just copy the color-name string
712 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
716 ParseTextAttribs (ColorClass cc, char *s)
718 (&appData.colorShout)[cc] = strdup(s);
722 ParseBoardSize (void *addr, char *name)
724 appData.boardSize = strdup(name);
729 { // In XBoard the sound-playing program takes care of obtaining the actual sound
733 SetCommPortDefaults ()
734 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
737 // [HGM] args: these three cases taken out to stay in front-end
739 SaveFontArg (FILE *f, ArgDescriptor *ad)
742 int i, n = (int)(intptr_t)ad->argLoc;
744 case 0: // CLOCK_FONT
745 name = appData.clockFont;
747 case 1: // MESSAGE_FONT
750 case 2: // COORD_FONT
751 name = appData.coordFont;
756 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
757 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
758 fontTable[n][squareSize] = strdup(name);
759 fontValid[n][squareSize] = True;
762 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
763 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
768 { // nothing to do, as the sounds are at all times represented by their text-string names already
772 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
773 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
774 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
778 SaveColor (FILE *f, ArgDescriptor *ad)
779 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
780 if(colorVariable[(int)(intptr_t)ad->argLoc])
781 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
785 SaveBoardSize (FILE *f, char *name, void *addr)
786 { // wrapper to shield back-end from BoardSize & sizeInfo
787 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
791 ParseCommPortSettings (char *s)
792 { // no such option in XBoard (yet)
798 GetActualPlacement (Widget wg, WindowPlacement *wp)
800 XWindowAttributes winAt;
807 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
808 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
809 wp->x = rx - winAt.x;
810 wp->y = ry - winAt.y;
811 wp->height = winAt.height;
812 wp->width = winAt.width;
813 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
818 { // wrapper to shield use of window handles from back-end (make addressible by number?)
819 // In XBoard this will have to wait until awareness of window parameters is implemented
820 GetActualPlacement(shellWidget, &wpMain);
821 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
822 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
823 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
824 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
825 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
826 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
830 PrintCommPortSettings (FILE *f, char *name)
831 { // This option does not exist in XBoard
835 EnsureOnScreen (int *x, int *y, int minX, int minY)
842 { // [HGM] args: allows testing if main window is realized from back-end
843 return xBoardWindow != 0;
847 PopUpStartupDialog ()
848 { // start menu not implemented in XBoard
852 ConvertToLine (int argc, char **argv)
854 static char line[128*1024], buf[1024];
858 for(i=1; i<argc; i++)
860 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
861 && argv[i][0] != '{' )
862 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
864 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
865 strncat(line, buf, 128*1024 - strlen(line) - 1 );
868 line[strlen(line)-1] = NULLCHAR;
872 //--------------------------------------------------------------------------------------------
875 ResizeBoardWindow (int w, int h, int inhibit)
877 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
879 shellArgs[0].value = w;
880 shellArgs[1].value = h;
881 shellArgs[4].value = shellArgs[2].value = w;
882 shellArgs[5].value = shellArgs[3].value = h;
883 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
885 XSync(xDisplay, False);
889 MakeOneColor (char *name, Pixel *color)
892 if (!appData.monoMode) {
893 vFrom.addr = (caddr_t) name;
894 vFrom.size = strlen(name);
895 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
896 if (vTo.addr == NULL) {
897 appData.monoMode = True;
900 *color = *(Pixel *) vTo.addr;
908 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
909 int forceMono = False;
911 if (appData.lowTimeWarning)
912 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
913 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
914 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
920 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
921 { // detervtomine what fonts to use, and create them
925 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
926 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
927 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
928 appData.font = fontTable[MESSAGE_FONT][squareSize];
929 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
930 appData.coordFont = fontTable[COORD_FONT][squareSize];
933 appData.font = InsertPxlSize(appData.font, fontPxlSize);
934 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
935 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
936 fontSet = CreateFontSet(appData.font);
937 clockFontSet = CreateFontSet(appData.clockFont);
939 /* For the coordFont, use the 0th font of the fontset. */
940 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
941 XFontStruct **font_struct_list;
942 XFontSetExtents *fontSize;
943 char **font_name_list;
944 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
945 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
946 coordFontStruct = XQueryFont(xDisplay, coordFontID);
947 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
948 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
951 appData.font = FindFont(appData.font, fontPxlSize);
952 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
953 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
954 clockFontID = XLoadFont(xDisplay, appData.clockFont);
955 clockFontStruct = XQueryFont(xDisplay, clockFontID);
956 coordFontID = XLoadFont(xDisplay, appData.coordFont);
957 coordFontStruct = XQueryFont(xDisplay, coordFontID);
958 // textHeight in !NLS mode!
960 countFontID = coordFontID; // [HGM] holdings
961 countFontStruct = coordFontStruct;
963 xdb = XtDatabase(xDisplay);
965 XrmPutLineResource(&xdb, "*international: True");
966 vTo.size = sizeof(XFontSet);
967 vTo.addr = (XtPointer) &fontSet;
968 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
970 XrmPutStringResource(&xdb, "*font", appData.font);
980 case ArgInt: p = " N"; break;
981 case ArgString: p = " STR"; break;
982 case ArgBoolean: p = " TF"; break;
983 case ArgSettingsFilename:
984 case ArgFilename: p = " FILE"; break;
985 case ArgX: p = " Nx"; break;
986 case ArgY: p = " Ny"; break;
987 case ArgAttribs: p = " TEXTCOL"; break;
988 case ArgColor: p = " COL"; break;
989 case ArgFont: p = " FONT"; break;
990 case ArgBoardSize: p = " SIZE"; break;
991 case ArgFloat: p = " FLOAT"; break;
996 case ArgCommSettings:
1007 ArgDescriptor *q, *p = argDescriptors+5;
1008 printf("\nXBoard accepts the following options:\n"
1009 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1010 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1011 " SIZE = board-size spec(s)\n"
1012 " Within parentheses are short forms, or options to set to true or false.\n"
1013 " Persistent options (saved in the settings file) are marked with *)\n\n");
1015 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1016 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1017 if(p->save) strcat(buf+len, "*");
1018 for(q=p+1; q->argLoc == p->argLoc; q++) {
1019 if(q->argName[0] == '-') continue;
1020 strcat(buf+len, q == p+1 ? " (" : " ");
1021 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1023 if(q != p+1) strcat(buf+len, ")");
1025 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1028 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1032 main (int argc, char **argv)
1034 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1035 XSetWindowAttributes window_attributes;
1037 Dimension boardWidth, boardHeight, w, h;
1039 int forceMono = False;
1041 srandom(time(0)); // [HGM] book: make random truly random
1043 setbuf(stdout, NULL);
1044 setbuf(stderr, NULL);
1047 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1048 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1052 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1057 programName = strrchr(argv[0], '/');
1058 if (programName == NULL)
1059 programName = argv[0];
1064 XtSetLanguageProc(NULL, NULL, NULL);
1065 if (appData.debugMode) {
1066 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1069 bindtextdomain(PACKAGE, LOCALEDIR);
1070 textdomain(PACKAGE);
1073 appData.boardSize = "";
1074 InitAppData(ConvertToLine(argc, argv));
1076 if (p == NULL) p = "/tmp";
1077 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1078 gameCopyFilename = (char*) malloc(i);
1079 gamePasteFilename = (char*) malloc(i);
1080 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1081 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1083 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1084 static char buf[MSG_SIZ];
1085 EscapeExpand(buf, appData.firstInitString);
1086 appData.firstInitString = strdup(buf);
1087 EscapeExpand(buf, appData.secondInitString);
1088 appData.secondInitString = strdup(buf);
1089 EscapeExpand(buf, appData.firstComputerString);
1090 appData.firstComputerString = strdup(buf);
1091 EscapeExpand(buf, appData.secondComputerString);
1092 appData.secondComputerString = strdup(buf);
1095 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1098 if (chdir(chessDir) != 0) {
1099 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1105 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1106 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1107 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1108 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1111 setbuf(debugFP, NULL);
1114 /* [HGM,HR] make sure board size is acceptable */
1115 if(appData.NrFiles > BOARD_FILES ||
1116 appData.NrRanks > BOARD_RANKS )
1117 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1120 /* This feature does not work; animation needs a rewrite */
1121 appData.highlightDragging = FALSE;
1125 gameInfo.variant = StringToVariant(appData.variant);
1126 InitPosition(FALSE);
1129 XtAppInitialize(&appContext, "XBoard", shellOptions,
1130 XtNumber(shellOptions),
1131 &argc, argv, xboardResources, NULL, 0);
1133 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1134 clientResources, XtNumber(clientResources),
1137 xDisplay = XtDisplay(shellWidget);
1138 xScreen = DefaultScreen(xDisplay);
1139 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1142 * determine size, based on supplied or remembered -size, or screen size
1144 if (isdigit(appData.boardSize[0])) {
1145 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1146 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1147 &fontPxlSize, &smallLayout, &tinyLayout);
1149 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1150 programName, appData.boardSize);
1154 /* Find some defaults; use the nearest known size */
1155 SizeDefaults *szd, *nearest;
1156 int distance = 99999;
1157 nearest = szd = sizeDefaults;
1158 while (szd->name != NULL) {
1159 if (abs(szd->squareSize - squareSize) < distance) {
1161 distance = abs(szd->squareSize - squareSize);
1162 if (distance == 0) break;
1166 if (i < 2) lineGap = nearest->lineGap;
1167 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1168 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1169 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1170 if (i < 6) smallLayout = nearest->smallLayout;
1171 if (i < 7) tinyLayout = nearest->tinyLayout;
1174 SizeDefaults *szd = sizeDefaults;
1175 if (*appData.boardSize == NULLCHAR) {
1176 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1177 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1180 if (szd->name == NULL) szd--;
1181 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1183 while (szd->name != NULL &&
1184 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1185 if (szd->name == NULL) {
1186 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1187 programName, appData.boardSize);
1191 squareSize = szd->squareSize;
1192 lineGap = szd->lineGap;
1193 clockFontPxlSize = szd->clockFontPxlSize;
1194 coordFontPxlSize = szd->coordFontPxlSize;
1195 fontPxlSize = szd->fontPxlSize;
1196 smallLayout = szd->smallLayout;
1197 tinyLayout = szd->tinyLayout;
1198 // [HGM] font: use defaults from settings file if available and not overruled
1201 defaultLineGap = lineGap;
1202 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1204 /* [HR] height treated separately (hacked) */
1205 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1206 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1209 * Determine what fonts to use.
1211 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1214 * Detect if there are not enough colors available and adapt.
1216 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1217 appData.monoMode = True;
1220 forceMono = MakeColors();
1223 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1225 appData.monoMode = True;
1228 if (appData.monoMode && appData.debugMode) {
1229 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1230 (unsigned long) XWhitePixel(xDisplay, xScreen),
1231 (unsigned long) XBlackPixel(xDisplay, xScreen));
1234 ParseIcsTextColors();
1236 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1242 layoutName = "tinyLayout";
1243 } else if (smallLayout) {
1244 layoutName = "smallLayout";
1246 layoutName = "normalLayout";
1249 optList = BoardPopUp(squareSize, lineGap, (void*)
1255 InitDrawingHandle(optList + W_BOARD);
1256 currBoard = &optList[W_BOARD];
1257 boardWidget = optList[W_BOARD].handle;
1258 menuBarWidget = optList[W_MENU].handle;
1259 dropMenu = optList[W_DROP].handle;
1260 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1261 formWidget = XtParent(boardWidget);
1262 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1263 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1264 XtGetValues(optList[W_WHITE].handle, args, 2);
1265 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1266 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1267 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1268 XtGetValues(optList[W_PAUSE].handle, args, 2);
1270 AppendEnginesToMenu(appData.recentEngineList);
1272 xBoardWindow = XtWindow(boardWidget);
1274 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1275 // not need to go into InitDrawingSizes().
1278 * Create X checkmark bitmap and initialize option menu checks.
1280 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1281 checkmark_bits, checkmark_width, checkmark_height);
1287 ReadBitmap(&wIconPixmap, "icon_white.bm",
1288 icon_white_bits, icon_white_width, icon_white_height);
1289 ReadBitmap(&bIconPixmap, "icon_black.bm",
1290 icon_black_bits, icon_black_width, icon_black_height);
1291 iconPixmap = wIconPixmap;
1293 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1294 XtSetValues(shellWidget, args, i);
1297 * Create a cursor for the board widget.
1299 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1300 XChangeWindowAttributes(xDisplay, xBoardWindow,
1301 CWCursor, &window_attributes);
1304 * Inhibit shell resizing.
1306 shellArgs[0].value = (XtArgVal) &w;
1307 shellArgs[1].value = (XtArgVal) &h;
1308 XtGetValues(shellWidget, shellArgs, 2);
1309 shellArgs[4].value = shellArgs[2].value = w;
1310 shellArgs[5].value = shellArgs[3].value = h;
1311 // XtSetValues(shellWidget, &shellArgs[2], 4);
1312 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1313 marginH = h - boardHeight;
1315 CatchDeleteWindow(shellWidget, "QuitProc");
1320 if(appData.logoSize)
1321 { // locate and read user logo
1323 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1324 ASSIGN(userLogo, buf);
1327 if (appData.animate || appData.animateDragging)
1330 XtAugmentTranslations(formWidget,
1331 XtParseTranslationTable(globalTranslations));
1333 XtAddEventHandler(formWidget, KeyPressMask, False,
1334 (XtEventHandler) MoveTypeInProc, NULL);
1335 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1336 (XtEventHandler) EventProc, NULL);
1338 /* [AS] Restore layout */
1339 if( wpMoveHistory.visible ) {
1343 if( wpEvalGraph.visible )
1348 if( wpEngineOutput.visible ) {
1349 EngineOutputPopUp();
1354 if (errorExitStatus == -1) {
1355 if (appData.icsActive) {
1356 /* We now wait until we see "login:" from the ICS before
1357 sending the logon script (problems with timestamp otherwise) */
1358 /*ICSInitScript();*/
1359 if (appData.icsInputBox) ICSInputBoxPopUp();
1363 signal(SIGWINCH, TermSizeSigHandler);
1365 signal(SIGINT, IntSigHandler);
1366 signal(SIGTERM, IntSigHandler);
1367 if (*appData.cmailGameName != NULLCHAR) {
1368 signal(SIGUSR1, CmailSigHandler);
1372 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1375 // XtSetKeyboardFocus(shellWidget, formWidget);
1376 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1378 XtAppMainLoop(appContext);
1379 if (appData.debugMode) fclose(debugFP); // [DM] debug
1384 TermSizeSigHandler (int sig)
1390 IntSigHandler (int sig)
1396 CmailSigHandler (int sig)
1401 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1403 /* Activate call-back function CmailSigHandlerCallBack() */
1404 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1406 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1410 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1413 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1415 /**** end signal code ****/
1418 #define Abs(n) ((n)<0 ? -(n) : (n))
1422 InsertPxlSize (char *pattern, int targetPxlSize)
1424 char *base_fnt_lst, strInt[12], *p, *q;
1425 int alternatives, i, len, strIntLen;
1428 * Replace the "*" (if present) in the pixel-size slot of each
1429 * alternative with the targetPxlSize.
1433 while ((p = strchr(p, ',')) != NULL) {
1437 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1438 strIntLen = strlen(strInt);
1439 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1443 while (alternatives--) {
1444 char *comma = strchr(p, ',');
1445 for (i=0; i<14; i++) {
1446 char *hyphen = strchr(p, '-');
1448 if (comma && hyphen > comma) break;
1449 len = hyphen + 1 - p;
1450 if (i == 7 && *p == '*' && len == 2) {
1452 memcpy(q, strInt, strIntLen);
1462 len = comma + 1 - p;
1469 return base_fnt_lst;
1473 CreateFontSet (char *base_fnt_lst)
1476 char **missing_list;
1480 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1481 &missing_list, &missing_count, &def_string);
1482 if (appData.debugMode) {
1484 XFontStruct **font_struct_list;
1485 char **font_name_list;
1486 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1488 fprintf(debugFP, " got list %s, locale %s\n",
1489 XBaseFontNameListOfFontSet(fntSet),
1490 XLocaleOfFontSet(fntSet));
1491 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1492 for (i = 0; i < count; i++) {
1493 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1496 for (i = 0; i < missing_count; i++) {
1497 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1500 if (fntSet == NULL) {
1501 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1506 #else // not ENABLE_NLS
1508 * Find a font that matches "pattern" that is as close as
1509 * possible to the targetPxlSize. Prefer fonts that are k
1510 * pixels smaller to fonts that are k pixels larger. The
1511 * pattern must be in the X Consortium standard format,
1512 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1513 * The return value should be freed with XtFree when no
1517 FindFont (char *pattern, int targetPxlSize)
1519 char **fonts, *p, *best, *scalable, *scalableTail;
1520 int i, j, nfonts, minerr, err, pxlSize;
1522 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1524 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1525 programName, pattern);
1532 for (i=0; i<nfonts; i++) {
1535 if (*p != '-') continue;
1537 if (*p == NULLCHAR) break;
1538 if (*p++ == '-') j++;
1540 if (j < 7) continue;
1543 scalable = fonts[i];
1546 err = pxlSize - targetPxlSize;
1547 if (Abs(err) < Abs(minerr) ||
1548 (minerr > 0 && err < 0 && -err == minerr)) {
1554 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1555 /* If the error is too big and there is a scalable font,
1556 use the scalable font. */
1557 int headlen = scalableTail - scalable;
1558 p = (char *) XtMalloc(strlen(scalable) + 10);
1559 while (isdigit(*scalableTail)) scalableTail++;
1560 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1562 p = (char *) XtMalloc(strlen(best) + 2);
1563 safeStrCpy(p, best, strlen(best)+1 );
1565 if (appData.debugMode) {
1566 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1567 pattern, targetPxlSize, p);
1569 XFreeFontNames(fonts);
1575 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1578 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1584 MarkMenuItem (char *menuRef, int state)
1586 MenuItem *item = MenuNameToItem(menuRef);
1590 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1591 XtSetValues(item->handle, args, 1);
1596 EnableNamedMenuItem (char *menuRef, int state)
1598 MenuItem *item = MenuNameToItem(menuRef);
1600 if(item) XtSetSensitive(item->handle, state);
1604 EnableButtonBar (int state)
1606 XtSetSensitive(optList[W_BUTTON].handle, state);
1611 SetMenuEnables (Enables *enab)
1613 while (enab->name != NULL) {
1614 EnableNamedMenuItem(enab->name, enab->value);
1620 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1621 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1623 if(*nprms == 0) return;
1624 item = MenuNameToItem(prms[0]);
1625 if(item) ((MenuProc *) item->proc) ();
1629 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
1631 RecentEngineEvent((int) (intptr_t) addr);
1635 AppendMenuItem (char *msg, int n)
1637 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
1649 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1650 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1651 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1652 dmEnables[i].piece);
1653 XtSetSensitive(entry, p != NULL || !appData.testLegality
1654 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1655 && !appData.icsActive));
1657 while (p && *p++ == dmEnables[i].piece) count++;
1658 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1660 XtSetArg(args[j], XtNlabel, label); j++;
1661 XtSetValues(entry, args, j);
1666 do_flash_delay (unsigned long msec)
1672 FlashDelay (int flash_delay)
1674 XSync(xDisplay, False);
1675 if(flash_delay) do_flash_delay(flash_delay);
1679 Fraction (int x, int start, int stop)
1681 double f = ((double) x - start)/(stop - start);
1682 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1686 static WindowPlacement wpNew;
1689 CoDrag (Widget sh, WindowPlacement *wp)
1692 int j=0, touch=0, fudge = 2;
1693 GetActualPlacement(sh, wp);
1694 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1695 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1696 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1697 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1698 if(!touch ) return; // only windows that touch co-move
1699 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1700 int heightInc = wpNew.height - wpMain.height;
1701 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1702 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1703 wp->y += fracTop * heightInc;
1704 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1705 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1706 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1707 int widthInc = wpNew.width - wpMain.width;
1708 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1709 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1710 wp->y += fracLeft * widthInc;
1711 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1712 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1714 wp->x += wpNew.x - wpMain.x;
1715 wp->y += wpNew.y - wpMain.y;
1716 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1717 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1718 XtSetArg(args[j], XtNx, wp->x); j++;
1719 XtSetArg(args[j], XtNy, wp->y); j++;
1720 XtSetValues(sh, args, j);
1724 ReSize (WindowPlacement *wp)
1727 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1728 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1729 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1730 if(sqy < sqx) sqx = sqy;
1731 if(sqx != squareSize) {
1732 squareSize = sqx; // adopt new square size
1733 CreatePNGPieces(); // make newly scaled pieces
1734 InitDrawingSizes(0, 0); // creates grid etc.
1735 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1736 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1737 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1738 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1739 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1742 static XtIntervalId delayedDragID = 0;
1751 GetActualPlacement(shellWidget, &wpNew);
1752 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1753 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1754 busy = 0; return; // false alarm
1757 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1758 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1759 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1760 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1762 DrawPosition(True, NULL);
1763 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1771 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1773 XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1777 EventProc (Widget widget, caddr_t unused, XEvent *event)
1779 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1780 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1784 * event handler for redrawing the board
1787 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1789 DrawPosition(True, NULL);
1794 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1795 { // [HGM] pv: walk PV
1796 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1799 static int savedIndex; /* gross that this is global */
1802 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1805 XawTextPosition index, dummy;
1808 XawTextGetSelectionPos(w, &index, &dummy);
1809 XtSetArg(arg, XtNstring, &val);
1810 XtGetValues(w, &arg, 1);
1811 ReplaceComment(savedIndex, val);
1812 if(savedIndex != currentMove) ToNrEvent(savedIndex);
1813 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1817 EditCommentPopUp (int index, char *title, char *text)
1820 if (text == NULL) text = "";
1821 NewCommentPopup(title, text, index);
1825 CommentPopUp (char *title, char *text)
1827 savedIndex = currentMove; // [HGM] vari
1828 NewCommentPopup(title, text, currentMove);
1834 PopDown(CommentDlg);
1838 /* Disable all user input other than deleting the window */
1839 static int frozen = 0;
1845 /* Grab by a widget that doesn't accept input */
1846 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1850 /* Undo a FreezeUI */
1854 if (!frozen) return;
1855 XtRemoveGrab(optList[W_MESSG].handle);
1863 static int oldPausing = FALSE;
1864 static GameMode oldmode = (GameMode) -1;
1867 if (!boardWidget || !XtIsRealized(boardWidget)) return;
1869 if (pausing != oldPausing) {
1870 oldPausing = pausing;
1871 MarkMenuItem("Mode.Pause", pausing);
1873 if (appData.showButtonBar) {
1874 /* Always toggle, don't set. Previous code messes up when
1875 invoked while the button is pressed, as releasing it
1876 toggles the state again. */
1879 XtSetArg(args[0], XtNbackground, &oldbg);
1880 XtSetArg(args[1], XtNforeground, &oldfg);
1881 XtGetValues(optList[W_PAUSE].handle,
1883 XtSetArg(args[0], XtNbackground, oldfg);
1884 XtSetArg(args[1], XtNforeground, oldbg);
1886 XtSetValues(optList[W_PAUSE].handle, args, 2);
1890 wname = ModeToWidgetName(oldmode);
1891 if (wname != NULL) {
1892 MarkMenuItem(wname, False);
1894 wname = ModeToWidgetName(gameMode);
1895 if (wname != NULL) {
1896 MarkMenuItem(wname, True);
1899 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1901 /* Maybe all the enables should be handled here, not just this one */
1902 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1904 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1909 * Button/menu procedures
1912 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1913 char *selected_fen_position=NULL;
1916 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1917 Atom *type_return, XtPointer *value_return,
1918 unsigned long *length_return, int *format_return)
1920 char *selection_tmp;
1922 // if (!selected_fen_position) return False; /* should never happen */
1923 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1924 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1925 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1928 if (f == NULL) return False;
1932 selection_tmp = XtMalloc(len + 1);
1933 count = fread(selection_tmp, 1, len, f);
1936 XtFree(selection_tmp);
1939 selection_tmp[len] = NULLCHAR;
1941 /* note: since no XtSelectionDoneProc was registered, Xt will
1942 * automatically call XtFree on the value returned. So have to
1943 * make a copy of it allocated with XtMalloc */
1944 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1945 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1948 *value_return=selection_tmp;
1949 *length_return=strlen(selection_tmp);
1950 *type_return=*target;
1951 *format_return = 8; /* bits per byte */
1953 } else if (*target == XA_TARGETS(xDisplay)) {
1954 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1955 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1956 targets_tmp[1] = XA_STRING;
1957 *value_return = targets_tmp;
1958 *type_return = XA_ATOM;
1961 // This code leads to a read of value_return out of bounds on 64-bit systems.
1962 // Other code which I have seen always sets *format_return to 32 independent of
1963 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1964 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1965 *format_return = 8 * sizeof(Atom);
1966 if (*format_return > 32) {
1967 *length_return *= *format_return / 32;
1968 *format_return = 32;
1971 *format_return = 32;
1979 /* note: when called from menu all parameters are NULL, so no clue what the
1980 * Widget which was clicked on was, or what the click event was
1983 CopySomething (char *src)
1985 selected_fen_position = src;
1987 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
1988 * have a notion of a position that is selected but not copied.
1989 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
1991 XtOwnSelection(menuBarWidget, XA_PRIMARY,
1993 SendPositionSelection,
1994 NULL/* lose_ownership_proc */ ,
1995 NULL/* transfer_done_proc */);
1996 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
1998 SendPositionSelection,
1999 NULL/* lose_ownership_proc */ ,
2000 NULL/* transfer_done_proc */);
2003 /* function called when the data to Paste is ready */
2005 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2006 Atom *type, XtPointer value, unsigned long *len, int *format)
2009 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2010 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2011 EditPositionPasteFEN(fenstr);
2015 /* called when Paste Position button is pressed,
2016 * all parameters will be NULL */
2018 PastePositionProc ()
2020 XtGetSelectionValue(menuBarWidget,
2021 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2022 /* (XtSelectionCallbackProc) */ PastePositionCB,
2023 NULL, /* client_data passed to PastePositionCB */
2025 /* better to use the time field from the event that triggered the
2026 * call to this function, but that isn't trivial to get
2033 /* note: when called from menu all parameters are NULL, so no clue what the
2034 * Widget which was clicked on was, or what the click event was
2036 /* function called when the data to Paste is ready */
2038 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2039 Atom *type, XtPointer value, unsigned long *len, int *format)
2042 if (value == NULL || *len == 0) {
2043 return; /* nothing had been selected to copy */
2045 f = fopen(gamePasteFilename, "w");
2047 DisplayError(_("Can't open temp file"), errno);
2050 fwrite(value, 1, *len, f);
2053 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2056 /* called when Paste Game button is pressed,
2057 * all parameters will be NULL */
2061 XtGetSelectionValue(menuBarWidget,
2062 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2063 /* (XtSelectionCallbackProc) */ PasteGameCB,
2064 NULL, /* client_data passed to PasteGameCB */
2066 /* better to use the time field from the event that triggered the
2067 * call to this function, but that isn't trivial to get
2076 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2083 { // bassic primitive for determining if modifier keys are pressed
2084 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2087 XQueryKeymap(xDisplay,keys);
2088 for(i=0; i<6; i++) {
2090 j = XKeysymToKeycode(xDisplay, codes[i]);
2091 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2097 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2101 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2102 if ( n == 1 && *buf >= 32 // printable
2103 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2104 ) BoxAutoPopUp (buf);
2108 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2109 { // [HGM] input: let up-arrow recall previous line from history
2114 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2115 { // [HGM] input: let down-arrow recall next line from history
2120 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2126 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2128 if (!TempBackwardActive) {
2129 TempBackwardActive = True;
2135 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2137 /* Check to see if triggered by a key release event for a repeating key.
2138 * If so the next queued event will be a key press of the same key at the same time */
2139 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2141 XPeekEvent(xDisplay, &next);
2142 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2143 next.xkey.keycode == event->xkey.keycode)
2147 TempBackwardActive = False;
2151 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2152 { // called as key binding
2155 if (nprms && *nprms > 0)
2159 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2165 { // called from menu
2166 ManInner(NULL, NULL, NULL, NULL);
2170 SetWindowTitle (char *text, char *title, char *icon)
2174 if (appData.titleInWindow) {
2176 XtSetArg(args[i], XtNlabel, text); i++;
2177 XtSetValues(titleWidget, args, i);
2180 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2181 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2182 XtSetValues(shellWidget, args, i);
2183 XSync(xDisplay, False);
2188 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2194 DisplayIcsInteractionTitle (String message)
2196 if (oldICSInteractionTitle == NULL) {
2197 /* Magic to find the old window title, adapted from vim */
2198 char *wina = getenv("WINDOWID");
2200 Window win = (Window) atoi(wina);
2201 Window root, parent, *children;
2202 unsigned int nchildren;
2203 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2205 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2206 if (!XQueryTree(xDisplay, win, &root, &parent,
2207 &children, &nchildren)) break;
2208 if (children) XFree((void *)children);
2209 if (parent == root || parent == 0) break;
2212 XSetErrorHandler(oldHandler);
2214 if (oldICSInteractionTitle == NULL) {
2215 oldICSInteractionTitle = "xterm";
2218 printf("\033]0;%s\007", message);
2223 XtIntervalId delayedEventTimerXID = 0;
2224 DelayedEventCallback delayedEventCallback = 0;
2229 delayedEventTimerXID = 0;
2230 delayedEventCallback();
2234 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2236 if(delayedEventTimerXID && delayedEventCallback == cb)
2237 // [HGM] alive: replace, rather than add or flush identical event
2238 XtRemoveTimeOut(delayedEventTimerXID);
2239 delayedEventCallback = cb;
2240 delayedEventTimerXID =
2241 XtAppAddTimeOut(appContext, millisec,
2242 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2245 DelayedEventCallback
2248 if (delayedEventTimerXID) {
2249 return delayedEventCallback;
2256 CancelDelayedEvent ()
2258 if (delayedEventTimerXID) {
2259 XtRemoveTimeOut(delayedEventTimerXID);
2260 delayedEventTimerXID = 0;
2264 XtIntervalId loadGameTimerXID = 0;
2267 LoadGameTimerRunning ()
2269 return loadGameTimerXID != 0;
2273 StopLoadGameTimer ()
2275 if (loadGameTimerXID != 0) {
2276 XtRemoveTimeOut(loadGameTimerXID);
2277 loadGameTimerXID = 0;
2285 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2287 loadGameTimerXID = 0;
2292 StartLoadGameTimer (long millisec)
2295 XtAppAddTimeOut(appContext, millisec,
2296 (XtTimerCallbackProc) LoadGameTimerCallback,
2300 XtIntervalId analysisClockXID = 0;
2303 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2305 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2306 || appData.icsEngineAnalyze) { // [DM]
2307 AnalysisPeriodicEvent(0);
2308 StartAnalysisClock();
2313 StartAnalysisClock ()
2316 XtAppAddTimeOut(appContext, 2000,
2317 (XtTimerCallbackProc) AnalysisClockCallback,
2321 XtIntervalId clockTimerXID = 0;
2324 ClockTimerRunning ()
2326 return clockTimerXID != 0;
2332 if (clockTimerXID != 0) {
2333 XtRemoveTimeOut(clockTimerXID);
2342 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2349 StartClockTimer (long millisec)
2352 XtAppAddTimeOut(appContext, millisec,
2353 (XtTimerCallbackProc) ClockTimerCallback,
2358 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2362 Widget w = (Widget) opt->handle;
2364 /* check for low time warning */
2365 Pixel foregroundOrWarningColor = timerForegroundPixel;
2368 appData.lowTimeWarning &&
2369 (timer / 1000) < appData.icsAlarmTime)
2370 foregroundOrWarningColor = lowTimeWarningColor;
2372 if (appData.clockMode) {
2373 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2374 XtSetArg(args[0], XtNlabel, buf);
2376 snprintf(buf, MSG_SIZ, "%s ", color);
2377 XtSetArg(args[0], XtNlabel, buf);
2382 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2383 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2385 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2386 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2389 XtSetValues(w, args, 3);
2392 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2395 SetClockIcon (int color)
2398 Pixmap pm = *clockIcons[color];
2399 if (iconPixmap != pm) {
2401 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2402 XtSetValues(shellWidget, args, 1);
2407 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2409 InputSource *is = (InputSource *) closure;
2414 if (is->lineByLine) {
2415 count = read(is->fd, is->unused,
2416 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2418 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2421 is->unused += count;
2423 while (p < is->unused) {
2424 q = memchr(p, '\n', is->unused - p);
2425 if (q == NULL) break;
2427 (is->func)(is, is->closure, p, q - p, 0);
2431 while (p < is->unused) {
2436 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2441 (is->func)(is, is->closure, is->buf, count, error);
2446 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2449 ChildProc *cp = (ChildProc *) pr;
2451 is = (InputSource *) calloc(1, sizeof(InputSource));
2452 is->lineByLine = lineByLine;
2456 is->fd = fileno(stdin);
2458 is->kind = cp->kind;
2459 is->fd = cp->fdFrom;
2462 is->unused = is->buf;
2465 is->xid = XtAppAddInput(appContext, is->fd,
2466 (XtPointer) (XtInputReadMask),
2467 (XtInputCallbackProc) DoInputCallback,
2469 is->closure = closure;
2470 return (InputSourceRef) is;
2474 RemoveInputSource (InputSourceRef isr)
2476 InputSource *is = (InputSource *) isr;
2478 if (is->xid == 0) return;
2479 XtRemoveInput(is->xid);
2485 static Boolean frameWaiting;
2488 FrameAlarm (int sig)
2490 frameWaiting = False;
2491 /* In case System-V style signals. Needed?? */
2492 signal(SIGALRM, FrameAlarm);
2496 FrameDelay (int time)
2498 struct itimerval delay;
2500 XSync(xDisplay, False);
2503 frameWaiting = True;
2504 signal(SIGALRM, FrameAlarm);
2505 delay.it_interval.tv_sec =
2506 delay.it_value.tv_sec = time / 1000;
2507 delay.it_interval.tv_usec =
2508 delay.it_value.tv_usec = (time % 1000) * 1000;
2509 setitimer(ITIMER_REAL, &delay, NULL);
2510 while (frameWaiting) pause();
2511 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2512 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2513 setitimer(ITIMER_REAL, &delay, NULL);
2520 FrameDelay (int time)
2522 XSync(xDisplay, False);
2524 usleep(time * 1000);
2530 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2532 char buf[MSG_SIZ], *logoName = buf;
2533 if(appData.logo[n][0]) {
2534 logoName = appData.logo[n];
2535 } else if(appData.autoLogo) {
2536 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2537 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2538 } else if(appData.directory[n] && appData.directory[n][0]) {
2539 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2543 { ASSIGN(cps->programLogo, logoName); }
2547 UpdateLogos (int displ)
2549 if(optList[W_WHITE-1].handle == NULL) return;
2550 LoadLogo(&first, 0, 0);
2551 LoadLogo(&second, 1, appData.icsActive);
2552 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);