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 update_ics_width P(());
264 int CopyMemoProc P(());
267 * XBoard depends on Xt R4 or higher
269 int xtVersion = XtSpecificationRelease;
274 Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
275 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
276 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
277 Option *optList; // contains all widgets of main window
279 XFontSet fontSet, clockFontSet;
282 XFontStruct *clockFontStruct;
284 Font coordFontID, countFontID;
285 XFontStruct *coordFontStruct, *countFontStruct;
286 XtAppContext appContext;
289 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
291 Position commentX = -1, commentY = -1;
292 Dimension commentW, commentH;
293 typedef unsigned int BoardSize;
295 Boolean chessProgram;
297 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
298 int smallLayout = 0, tinyLayout = 0,
299 marginW, marginH, // [HGM] for run-time resizing
300 fromX = -1, fromY = -1, toX, toY, commentUp = False,
301 errorExitStatus = -1, defaultLineGap;
302 Dimension textHeight;
303 Pixel timerForegroundPixel, timerBackgroundPixel;
304 Pixel buttonForegroundPixel, buttonBackgroundPixel;
305 char *chessDir, *programName, *programVersion;
306 Boolean alwaysOnTop = False;
307 char *icsTextMenuString;
309 char *firstChessProgramNames;
310 char *secondChessProgramNames;
312 WindowPlacement wpMain;
313 WindowPlacement wpConsole;
314 WindowPlacement wpComment;
315 WindowPlacement wpMoveHistory;
316 WindowPlacement wpEvalGraph;
317 WindowPlacement wpEngineOutput;
318 WindowPlacement wpGameList;
319 WindowPlacement wpTags;
322 /* This magic number is the number of intermediate frames used
323 in each half of the animation. For short moves it's reduced
324 by 1. The total number of frames will be factor * 2 + 1. */
327 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
334 DropMenuEnables dmEnables[] = {
351 XtResource clientResources[] = {
352 { "flashCount", "flashCount", XtRInt, sizeof(int),
353 XtOffset(AppDataPtr, flashCount), XtRImmediate,
354 (XtPointer) FLASH_COUNT },
357 XrmOptionDescRec shellOptions[] = {
358 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
359 { "-flash", "flashCount", XrmoptionNoArg, "3" },
360 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
363 XtActionsRec boardActions[] = {
364 { "DrawPosition", DrawPositionProc },
365 { "HandlePV", HandlePV },
366 { "SelectPV", SelectPV },
367 { "StopPV", StopPV },
368 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
369 { "QuitProc", QuitWrapper },
370 { "ManProc", ManInner },
371 { "TempBackwardProc", TempBackwardProc },
372 { "TempForwardProc", TempForwardProc },
373 { "CommentClick", (XtActionProc) CommentClick },
374 { "GenericPopDown", (XtActionProc) GenericPopDown },
375 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
376 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
377 { "SelectMove", (XtActionProc) SelectMoveX },
378 { "LoadSelectedProc", LoadSelectedProc },
379 { "SetFilterProc", SetFilterProc },
380 { "TypeInProc", TypeInProc },
381 { "EnterKeyProc", EnterKeyProc },
382 { "UpKeyProc", UpKeyProc },
383 { "DownKeyProc", DownKeyProc },
384 { "WheelProc", WheelProc },
385 { "TabProc", TabProc },
388 char globalTranslations[] =
389 ":<Key>F9: MenuItem(Actions.Resign) \n \
390 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
391 :Meta<Key>V: MenuItem(File.NewVariant) \n \
392 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
393 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
394 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
395 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
396 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
397 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
398 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
399 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
400 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
401 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
402 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
403 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
404 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
405 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
406 :Ctrl<Key>q: MenuItem(File.Quit) \n \
407 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
408 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
409 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
410 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
411 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
412 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
413 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
414 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
415 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
416 :Meta<Key>G: MenuItem(View.GameList) \n \
417 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
418 :<Key>Pause: MenuItem(Mode.Pause) \n \
419 :<Key>F3: MenuItem(Action.Accept) \n \
420 :<Key>F4: MenuItem(Action.Decline) \n \
421 :<Key>F12: MenuItem(Action.Rematch) \n \
422 :<Key>F5: MenuItem(Action.CallFlag) \n \
423 :<Key>F6: MenuItem(Action.Draw) \n \
424 :<Key>F7: MenuItem(Action.Adjourn) \n \
425 :<Key>F8: MenuItem(Action.Abort) \n \
426 :<Key>F10: MenuItem(Action.StopObserving) \n \
427 :<Key>F11: MenuItem(Action.StopExamining) \n \
428 :Ctrl<Key>d: MenuItem(DebugProc) \n \
429 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
430 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
431 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
432 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
433 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
434 :<Key>Left: MenuItem(Edit.Backward) \n \
435 :<Key>Right: MenuItem(Edit.Forward) \n \
436 :<Key>Home: MenuItem(Edit.Revert) \n \
437 :<Key>End: MenuItem(Edit.TruncateGame) \n \
438 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
439 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
440 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
441 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
442 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
443 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
444 #ifndef OPTIONSDIALOG
446 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
447 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
448 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
449 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
450 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
453 :<Key>F1: MenuItem(Help.ManXBoard) \n \
454 :<Key>F2: MenuItem(View.FlipView) \n \
455 :<KeyDown>Return: TempBackwardProc() \n \
456 :<KeyUp>Return: TempForwardProc() \n";
458 char ICSInputTranslations[] =
459 "<Key>Up: UpKeyProc() \n "
460 "<Key>Down: DownKeyProc() \n "
461 "<Key>Return: EnterKeyProc() \n";
463 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
464 // as the widget is destroyed before the up-click can call extend-end
465 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
467 String xboardResources[] = {
468 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
473 /* Max possible square size */
474 #define MAXSQSIZE 256
476 static int xpm_avail[MAXSQSIZE];
478 #ifdef HAVE_DIR_STRUCT
480 /* Extract piece size from filename */
482 xpm_getsize (char *name, int len, char *ext)
490 if ((p=strchr(name, '.')) == NULL ||
491 StrCaseCmp(p+1, ext) != 0)
497 while (*p && isdigit(*p))
504 /* Setup xpm_avail */
506 xpm_getavail (char *dirname, char *ext)
512 for (i=0; i<MAXSQSIZE; ++i)
515 if (appData.debugMode)
516 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
518 dir = opendir(dirname);
521 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
522 programName, dirname);
526 while ((ent=readdir(dir)) != NULL) {
527 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
528 if (i > 0 && i < MAXSQSIZE)
538 xpm_print_avail (FILE *fp, char *ext)
542 fprintf(fp, _("Available `%s' sizes:\n"), ext);
543 for (i=1; i<MAXSQSIZE; ++i) {
549 /* Return XPM piecesize closest to size */
551 xpm_closest_to (char *dirname, int size, char *ext)
554 int sm_diff = MAXSQSIZE;
558 xpm_getavail(dirname, ext);
560 if (appData.debugMode)
561 xpm_print_avail(stderr, ext);
563 for (i=1; i<MAXSQSIZE; ++i) {
566 diff = (diff<0) ? -diff : diff;
567 if (diff < sm_diff) {
575 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
581 #else /* !HAVE_DIR_STRUCT */
582 /* If we are on a system without a DIR struct, we can't
583 read the directory, so we can't collect a list of
584 filenames, etc., so we can't do any size-fitting. */
586 xpm_closest_to (char *dirname, int size, char *ext)
589 Warning: No DIR structure found on this system --\n\
590 Unable to autosize for XPM/XIM pieces.\n\
591 Please report this error to %s.\n\
592 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
595 #endif /* HAVE_DIR_STRUCT */
598 /* Arrange to catch delete-window events */
599 Atom wm_delete_window;
601 CatchDeleteWindow (Widget w, String procname)
604 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
605 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
606 XtAugmentTranslations(w, XtParseTranslationTable(buf));
613 XtSetArg(args[0], XtNiconic, False);
614 XtSetValues(shellWidget, args, 1);
616 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
619 //---------------------------------------------------------------------------------------------------------
620 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
623 #define CW_USEDEFAULT (1<<31)
624 #define ICS_TEXT_MENU_SIZE 90
625 #define DEBUG_FILE "xboard.debug"
626 #define SetCurrentDirectory chdir
627 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
631 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
634 // front-end part of option handling
636 // [HGM] This platform-dependent table provides the location for storing the color info
637 extern char *crWhite, * crBlack;
641 &appData.whitePieceColor,
642 &appData.blackPieceColor,
643 &appData.lightSquareColor,
644 &appData.darkSquareColor,
645 &appData.highlightSquareColor,
646 &appData.premoveHighlightColor,
647 &appData.lowTimeWarningColor,
658 // [HGM] font: keep a font for each square size, even non-stndard ones
661 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
662 char *fontTable[NUM_FONTS][MAX_SIZE];
665 ParseFont (char *name, int number)
666 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
668 if(sscanf(name, "size%d:", &size)) {
669 // [HGM] font: font is meant for specific boardSize (likely from settings file);
670 // defer processing it until we know if it matches our board size
671 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
672 fontTable[number][size] = strdup(strchr(name, ':')+1);
673 fontValid[number][size] = True;
678 case 0: // CLOCK_FONT
679 appData.clockFont = strdup(name);
681 case 1: // MESSAGE_FONT
682 appData.font = strdup(name);
684 case 2: // COORD_FONT
685 appData.coordFont = strdup(name);
690 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
695 { // only 2 fonts currently
696 appData.clockFont = CLOCK_FONT_NAME;
697 appData.coordFont = COORD_FONT_NAME;
698 appData.font = DEFAULT_FONT_NAME;
703 { // no-op, until we identify the code for this already in XBoard and move it here
707 ParseColor (int n, char *name)
708 { // in XBoard, just copy the color-name string
709 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
713 ParseTextAttribs (ColorClass cc, char *s)
715 (&appData.colorShout)[cc] = strdup(s);
719 ParseBoardSize (void *addr, char *name)
721 appData.boardSize = strdup(name);
726 { // In XBoard the sound-playing program takes care of obtaining the actual sound
730 SetCommPortDefaults ()
731 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
734 // [HGM] args: these three cases taken out to stay in front-end
736 SaveFontArg (FILE *f, ArgDescriptor *ad)
739 int i, n = (int)(intptr_t)ad->argLoc;
741 case 0: // CLOCK_FONT
742 name = appData.clockFont;
744 case 1: // MESSAGE_FONT
747 case 2: // COORD_FONT
748 name = appData.coordFont;
753 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
754 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
755 fontTable[n][squareSize] = strdup(name);
756 fontValid[n][squareSize] = True;
759 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
760 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
765 { // nothing to do, as the sounds are at all times represented by their text-string names already
769 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
770 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
771 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
775 SaveColor (FILE *f, ArgDescriptor *ad)
776 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
777 if(colorVariable[(int)(intptr_t)ad->argLoc])
778 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
782 SaveBoardSize (FILE *f, char *name, void *addr)
783 { // wrapper to shield back-end from BoardSize & sizeInfo
784 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
788 ParseCommPortSettings (char *s)
789 { // no such option in XBoard (yet)
795 GetActualPlacement (Widget wg, WindowPlacement *wp)
797 XWindowAttributes winAt;
804 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
805 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
806 wp->x = rx - winAt.x;
807 wp->y = ry - winAt.y;
808 wp->height = winAt.height;
809 wp->width = winAt.width;
810 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
815 { // wrapper to shield use of window handles from back-end (make addressible by number?)
816 // In XBoard this will have to wait until awareness of window parameters is implemented
817 GetActualPlacement(shellWidget, &wpMain);
818 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
819 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
820 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
821 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
822 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
823 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
827 PrintCommPortSettings (FILE *f, char *name)
828 { // This option does not exist in XBoard
832 EnsureOnScreen (int *x, int *y, int minX, int minY)
839 { // [HGM] args: allows testing if main window is realized from back-end
840 return xBoardWindow != 0;
844 PopUpStartupDialog ()
845 { // start menu not implemented in XBoard
849 ConvertToLine (int argc, char **argv)
851 static char line[128*1024], buf[1024];
855 for(i=1; i<argc; i++)
857 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
858 && argv[i][0] != '{' )
859 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
861 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
862 strncat(line, buf, 128*1024 - strlen(line) - 1 );
865 line[strlen(line)-1] = NULLCHAR;
869 //--------------------------------------------------------------------------------------------
872 ResizeBoardWindow (int w, int h, int inhibit)
874 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
876 shellArgs[0].value = w;
877 shellArgs[1].value = h;
878 shellArgs[4].value = shellArgs[2].value = w;
879 shellArgs[5].value = shellArgs[3].value = h;
880 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
882 XSync(xDisplay, False);
886 MakeOneColor (char *name, Pixel *color)
889 if (!appData.monoMode) {
890 vFrom.addr = (caddr_t) name;
891 vFrom.size = strlen(name);
892 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
893 if (vTo.addr == NULL) {
894 appData.monoMode = True;
897 *color = *(Pixel *) vTo.addr;
905 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
906 int forceMono = False;
908 if (appData.lowTimeWarning)
909 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
910 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
911 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
917 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
918 { // detervtomine what fonts to use, and create them
922 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
923 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
924 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
925 appData.font = fontTable[MESSAGE_FONT][squareSize];
926 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
927 appData.coordFont = fontTable[COORD_FONT][squareSize];
930 appData.font = InsertPxlSize(appData.font, fontPxlSize);
931 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
932 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
933 fontSet = CreateFontSet(appData.font);
934 clockFontSet = CreateFontSet(appData.clockFont);
936 /* For the coordFont, use the 0th font of the fontset. */
937 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
938 XFontStruct **font_struct_list;
939 XFontSetExtents *fontSize;
940 char **font_name_list;
941 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
942 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
943 coordFontStruct = XQueryFont(xDisplay, coordFontID);
944 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
945 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
948 appData.font = FindFont(appData.font, fontPxlSize);
949 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
950 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
951 clockFontID = XLoadFont(xDisplay, appData.clockFont);
952 clockFontStruct = XQueryFont(xDisplay, clockFontID);
953 coordFontID = XLoadFont(xDisplay, appData.coordFont);
954 coordFontStruct = XQueryFont(xDisplay, coordFontID);
955 // textHeight in !NLS mode!
957 countFontID = coordFontID; // [HGM] holdings
958 countFontStruct = coordFontStruct;
960 xdb = XtDatabase(xDisplay);
962 XrmPutLineResource(&xdb, "*international: True");
963 vTo.size = sizeof(XFontSet);
964 vTo.addr = (XtPointer) &fontSet;
965 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
967 XrmPutStringResource(&xdb, "*font", appData.font);
977 case ArgInt: p = " N"; break;
978 case ArgString: p = " STR"; break;
979 case ArgBoolean: p = " TF"; break;
980 case ArgSettingsFilename:
981 case ArgFilename: p = " FILE"; break;
982 case ArgX: p = " Nx"; break;
983 case ArgY: p = " Ny"; break;
984 case ArgAttribs: p = " TEXTCOL"; break;
985 case ArgColor: p = " COL"; break;
986 case ArgFont: p = " FONT"; break;
987 case ArgBoardSize: p = " SIZE"; break;
988 case ArgFloat: p = " FLOAT"; break;
993 case ArgCommSettings:
1004 ArgDescriptor *q, *p = argDescriptors+5;
1005 printf("\nXBoard accepts the following options:\n"
1006 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1007 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1008 " SIZE = board-size spec(s)\n"
1009 " Within parentheses are short forms, or options to set to true or false.\n"
1010 " Persistent options (saved in the settings file) are marked with *)\n\n");
1012 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1013 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1014 if(p->save) strcat(buf+len, "*");
1015 for(q=p+1; q->argLoc == p->argLoc; q++) {
1016 if(q->argName[0] == '-') continue;
1017 strcat(buf+len, q == p+1 ? " (" : " ");
1018 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1020 if(q != p+1) strcat(buf+len, ")");
1022 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1025 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1029 main (int argc, char **argv)
1031 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1032 XSetWindowAttributes window_attributes;
1034 Dimension boardWidth, boardHeight, w, h;
1036 int forceMono = False;
1038 srandom(time(0)); // [HGM] book: make random truly random
1040 setbuf(stdout, NULL);
1041 setbuf(stderr, NULL);
1044 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1045 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1049 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1054 programName = strrchr(argv[0], '/');
1055 if (programName == NULL)
1056 programName = argv[0];
1061 XtSetLanguageProc(NULL, NULL, NULL);
1062 if (appData.debugMode) {
1063 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1066 bindtextdomain(PACKAGE, LOCALEDIR);
1067 textdomain(PACKAGE);
1070 appData.boardSize = "";
1071 InitAppData(ConvertToLine(argc, argv));
1073 if (p == NULL) p = "/tmp";
1074 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1075 gameCopyFilename = (char*) malloc(i);
1076 gamePasteFilename = (char*) malloc(i);
1077 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1078 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1080 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1081 static char buf[MSG_SIZ];
1082 EscapeExpand(buf, appData.firstInitString);
1083 appData.firstInitString = strdup(buf);
1084 EscapeExpand(buf, appData.secondInitString);
1085 appData.secondInitString = strdup(buf);
1086 EscapeExpand(buf, appData.firstComputerString);
1087 appData.firstComputerString = strdup(buf);
1088 EscapeExpand(buf, appData.secondComputerString);
1089 appData.secondComputerString = strdup(buf);
1092 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1095 if (chdir(chessDir) != 0) {
1096 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1102 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1103 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1104 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1105 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1108 setbuf(debugFP, NULL);
1111 /* [HGM,HR] make sure board size is acceptable */
1112 if(appData.NrFiles > BOARD_FILES ||
1113 appData.NrRanks > BOARD_RANKS )
1114 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1117 /* This feature does not work; animation needs a rewrite */
1118 appData.highlightDragging = FALSE;
1122 gameInfo.variant = StringToVariant(appData.variant);
1123 InitPosition(FALSE);
1126 XtAppInitialize(&appContext, "XBoard", shellOptions,
1127 XtNumber(shellOptions),
1128 &argc, argv, xboardResources, NULL, 0);
1130 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1131 clientResources, XtNumber(clientResources),
1134 xDisplay = XtDisplay(shellWidget);
1135 xScreen = DefaultScreen(xDisplay);
1136 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1139 * determine size, based on supplied or remembered -size, or screen size
1141 if (isdigit(appData.boardSize[0])) {
1142 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1143 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1144 &fontPxlSize, &smallLayout, &tinyLayout);
1146 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1147 programName, appData.boardSize);
1151 /* Find some defaults; use the nearest known size */
1152 SizeDefaults *szd, *nearest;
1153 int distance = 99999;
1154 nearest = szd = sizeDefaults;
1155 while (szd->name != NULL) {
1156 if (abs(szd->squareSize - squareSize) < distance) {
1158 distance = abs(szd->squareSize - squareSize);
1159 if (distance == 0) break;
1163 if (i < 2) lineGap = nearest->lineGap;
1164 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1165 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1166 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1167 if (i < 6) smallLayout = nearest->smallLayout;
1168 if (i < 7) tinyLayout = nearest->tinyLayout;
1171 SizeDefaults *szd = sizeDefaults;
1172 if (*appData.boardSize == NULLCHAR) {
1173 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1174 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1177 if (szd->name == NULL) szd--;
1178 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1180 while (szd->name != NULL &&
1181 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1182 if (szd->name == NULL) {
1183 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1184 programName, appData.boardSize);
1188 squareSize = szd->squareSize;
1189 lineGap = szd->lineGap;
1190 clockFontPxlSize = szd->clockFontPxlSize;
1191 coordFontPxlSize = szd->coordFontPxlSize;
1192 fontPxlSize = szd->fontPxlSize;
1193 smallLayout = szd->smallLayout;
1194 tinyLayout = szd->tinyLayout;
1195 // [HGM] font: use defaults from settings file if available and not overruled
1198 defaultLineGap = lineGap;
1199 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1201 /* [HR] height treated separately (hacked) */
1202 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1203 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1206 * Determine what fonts to use.
1208 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1211 * Detect if there are not enough colors available and adapt.
1213 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1214 appData.monoMode = True;
1217 forceMono = MakeColors();
1220 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1222 appData.monoMode = True;
1225 if (appData.monoMode && appData.debugMode) {
1226 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1227 (unsigned long) XWhitePixel(xDisplay, xScreen),
1228 (unsigned long) XBlackPixel(xDisplay, xScreen));
1231 ParseIcsTextColors();
1233 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1239 layoutName = "tinyLayout";
1240 } else if (smallLayout) {
1241 layoutName = "smallLayout";
1243 layoutName = "normalLayout";
1246 optList = BoardPopUp(squareSize, lineGap, (void*)
1252 InitDrawingHandle(optList + W_BOARD);
1253 currBoard = &optList[W_BOARD];
1254 boardWidget = optList[W_BOARD].handle;
1255 menuBarWidget = optList[W_MENU].handle;
1256 dropMenu = optList[W_DROP].handle;
1257 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1258 formWidget = XtParent(boardWidget);
1259 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1260 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1261 XtGetValues(optList[W_WHITE].handle, args, 2);
1262 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1263 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1264 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1265 XtGetValues(optList[W_PAUSE].handle, args, 2);
1268 xBoardWindow = XtWindow(boardWidget);
1270 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1271 // not need to go into InitDrawingSizes().
1274 * Create X checkmark bitmap and initialize option menu checks.
1276 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1277 checkmark_bits, checkmark_width, checkmark_height);
1283 ReadBitmap(&wIconPixmap, "icon_white.bm",
1284 icon_white_bits, icon_white_width, icon_white_height);
1285 ReadBitmap(&bIconPixmap, "icon_black.bm",
1286 icon_black_bits, icon_black_width, icon_black_height);
1287 iconPixmap = wIconPixmap;
1289 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1290 XtSetValues(shellWidget, args, i);
1293 * Create a cursor for the board widget.
1295 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1296 XChangeWindowAttributes(xDisplay, xBoardWindow,
1297 CWCursor, &window_attributes);
1300 * Inhibit shell resizing.
1302 shellArgs[0].value = (XtArgVal) &w;
1303 shellArgs[1].value = (XtArgVal) &h;
1304 XtGetValues(shellWidget, shellArgs, 2);
1305 shellArgs[4].value = shellArgs[2].value = w;
1306 shellArgs[5].value = shellArgs[3].value = h;
1307 // XtSetValues(shellWidget, &shellArgs[2], 4);
1308 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1309 marginH = h - boardHeight;
1311 CatchDeleteWindow(shellWidget, "QuitProc");
1316 if(appData.logoSize)
1317 { // locate and read user logo
1319 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1320 ASSIGN(userLogo, buf);
1323 if (appData.animate || appData.animateDragging)
1326 XtAugmentTranslations(formWidget,
1327 XtParseTranslationTable(globalTranslations));
1329 XtAddEventHandler(formWidget, KeyPressMask, False,
1330 (XtEventHandler) MoveTypeInProc, NULL);
1331 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1332 (XtEventHandler) EventProc, NULL);
1334 /* [AS] Restore layout */
1335 if( wpMoveHistory.visible ) {
1339 if( wpEvalGraph.visible )
1344 if( wpEngineOutput.visible ) {
1345 EngineOutputPopUp();
1350 if (errorExitStatus == -1) {
1351 if (appData.icsActive) {
1352 /* We now wait until we see "login:" from the ICS before
1353 sending the logon script (problems with timestamp otherwise) */
1354 /*ICSInitScript();*/
1355 if (appData.icsInputBox) ICSInputBoxPopUp();
1359 signal(SIGWINCH, TermSizeSigHandler);
1361 signal(SIGINT, IntSigHandler);
1362 signal(SIGTERM, IntSigHandler);
1363 if (*appData.cmailGameName != NULLCHAR) {
1364 signal(SIGUSR1, CmailSigHandler);
1368 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1371 // XtSetKeyboardFocus(shellWidget, formWidget);
1372 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1374 XtAppMainLoop(appContext);
1375 if (appData.debugMode) fclose(debugFP); // [DM] debug
1380 TermSizeSigHandler (int sig)
1386 IntSigHandler (int sig)
1392 CmailSigHandler (int sig)
1397 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1399 /* Activate call-back function CmailSigHandlerCallBack() */
1400 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1402 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1406 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1409 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1411 /**** end signal code ****/
1414 #define Abs(n) ((n)<0 ? -(n) : (n))
1418 InsertPxlSize (char *pattern, int targetPxlSize)
1420 char *base_fnt_lst, strInt[12], *p, *q;
1421 int alternatives, i, len, strIntLen;
1424 * Replace the "*" (if present) in the pixel-size slot of each
1425 * alternative with the targetPxlSize.
1429 while ((p = strchr(p, ',')) != NULL) {
1433 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1434 strIntLen = strlen(strInt);
1435 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1439 while (alternatives--) {
1440 char *comma = strchr(p, ',');
1441 for (i=0; i<14; i++) {
1442 char *hyphen = strchr(p, '-');
1444 if (comma && hyphen > comma) break;
1445 len = hyphen + 1 - p;
1446 if (i == 7 && *p == '*' && len == 2) {
1448 memcpy(q, strInt, strIntLen);
1458 len = comma + 1 - p;
1465 return base_fnt_lst;
1469 CreateFontSet (char *base_fnt_lst)
1472 char **missing_list;
1476 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1477 &missing_list, &missing_count, &def_string);
1478 if (appData.debugMode) {
1480 XFontStruct **font_struct_list;
1481 char **font_name_list;
1482 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1484 fprintf(debugFP, " got list %s, locale %s\n",
1485 XBaseFontNameListOfFontSet(fntSet),
1486 XLocaleOfFontSet(fntSet));
1487 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1488 for (i = 0; i < count; i++) {
1489 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1492 for (i = 0; i < missing_count; i++) {
1493 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1496 if (fntSet == NULL) {
1497 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1502 #else // not ENABLE_NLS
1504 * Find a font that matches "pattern" that is as close as
1505 * possible to the targetPxlSize. Prefer fonts that are k
1506 * pixels smaller to fonts that are k pixels larger. The
1507 * pattern must be in the X Consortium standard format,
1508 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1509 * The return value should be freed with XtFree when no
1513 FindFont (char *pattern, int targetPxlSize)
1515 char **fonts, *p, *best, *scalable, *scalableTail;
1516 int i, j, nfonts, minerr, err, pxlSize;
1518 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1520 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1521 programName, pattern);
1528 for (i=0; i<nfonts; i++) {
1531 if (*p != '-') continue;
1533 if (*p == NULLCHAR) break;
1534 if (*p++ == '-') j++;
1536 if (j < 7) continue;
1539 scalable = fonts[i];
1542 err = pxlSize - targetPxlSize;
1543 if (Abs(err) < Abs(minerr) ||
1544 (minerr > 0 && err < 0 && -err == minerr)) {
1550 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1551 /* If the error is too big and there is a scalable font,
1552 use the scalable font. */
1553 int headlen = scalableTail - scalable;
1554 p = (char *) XtMalloc(strlen(scalable) + 10);
1555 while (isdigit(*scalableTail)) scalableTail++;
1556 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1558 p = (char *) XtMalloc(strlen(best) + 2);
1559 safeStrCpy(p, best, strlen(best)+1 );
1561 if (appData.debugMode) {
1562 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1563 pattern, targetPxlSize, p);
1565 XFreeFontNames(fonts);
1571 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1574 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1580 MarkMenuItem (char *menuRef, int state)
1582 MenuItem *item = MenuNameToItem(menuRef);
1586 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1587 XtSetValues(item->handle, args, 1);
1592 EnableNamedMenuItem (char *menuRef, int state)
1594 MenuItem *item = MenuNameToItem(menuRef);
1596 if(item) XtSetSensitive(item->handle, state);
1600 EnableButtonBar (int state)
1602 XtSetSensitive(optList[W_BUTTON].handle, state);
1607 SetMenuEnables (Enables *enab)
1609 while (enab->name != NULL) {
1610 EnableNamedMenuItem(enab->name, enab->value);
1616 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1617 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1619 if(*nprms == 0) return;
1620 item = MenuNameToItem(prms[0]);
1621 if(item) ((MenuProc *) item->proc) ();
1633 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1634 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1635 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1636 dmEnables[i].piece);
1637 XtSetSensitive(entry, p != NULL || !appData.testLegality
1638 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1639 && !appData.icsActive));
1641 while (p && *p++ == dmEnables[i].piece) count++;
1642 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1644 XtSetArg(args[j], XtNlabel, label); j++;
1645 XtSetValues(entry, args, j);
1650 do_flash_delay (unsigned long msec)
1656 FlashDelay (int flash_delay)
1658 XSync(xDisplay, False);
1659 if(flash_delay) do_flash_delay(flash_delay);
1663 Fraction (int x, int start, int stop)
1665 double f = ((double) x - start)/(stop - start);
1666 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1670 static WindowPlacement wpNew;
1673 CoDrag (Widget sh, WindowPlacement *wp)
1676 int j=0, touch=0, fudge = 2;
1677 GetActualPlacement(sh, wp);
1678 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1679 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1680 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1681 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1682 if(!touch ) return; // only windows that touch co-move
1683 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1684 int heightInc = wpNew.height - wpMain.height;
1685 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1686 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1687 wp->y += fracTop * heightInc;
1688 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1689 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1690 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1691 int widthInc = wpNew.width - wpMain.width;
1692 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1693 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1694 wp->y += fracLeft * widthInc;
1695 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1696 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1698 wp->x += wpNew.x - wpMain.x;
1699 wp->y += wpNew.y - wpMain.y;
1700 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1701 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1702 XtSetArg(args[j], XtNx, wp->x); j++;
1703 XtSetArg(args[j], XtNy, wp->y); j++;
1704 XtSetValues(sh, args, j);
1708 ReSize (WindowPlacement *wp)
1711 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1712 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1713 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1714 if(sqy < sqx) sqx = sqy;
1715 if(sqx != squareSize) {
1716 squareSize = sqx; // adopt new square size
1717 CreatePNGPieces(); // make newly scaled pieces
1718 InitDrawingSizes(0, 0); // creates grid etc.
1719 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1720 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1721 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1722 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1723 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1726 static XtIntervalId delayedDragID = 0;
1735 GetActualPlacement(shellWidget, &wpNew);
1736 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1737 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1738 busy = 0; return; // false alarm
1741 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1742 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1743 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1744 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1746 DrawPosition(True, NULL);
1747 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1755 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1757 XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1761 EventProc (Widget widget, caddr_t unused, XEvent *event)
1763 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1764 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1768 * event handler for redrawing the board
1771 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1773 DrawPosition(True, NULL);
1778 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1779 { // [HGM] pv: walk PV
1780 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1783 extern int savedIndex; /* gross that this is global */
1786 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1789 XawTextPosition index, dummy;
1792 XawTextGetSelectionPos(w, &index, &dummy);
1793 XtSetArg(arg, XtNstring, &val);
1794 XtGetValues(w, &arg, 1);
1795 ReplaceComment(savedIndex, val);
1796 if(savedIndex != currentMove) ToNrEvent(savedIndex);
1797 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1801 /* Disable all user input other than deleting the window */
1802 static int frozen = 0;
1808 /* Grab by a widget that doesn't accept input */
1809 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1813 /* Undo a FreezeUI */
1817 if (!frozen) return;
1818 XtRemoveGrab(optList[W_MESSG].handle);
1826 static int oldPausing = FALSE;
1827 static GameMode oldmode = (GameMode) -1;
1830 if (!boardWidget || !XtIsRealized(boardWidget)) return;
1832 if (pausing != oldPausing) {
1833 oldPausing = pausing;
1834 MarkMenuItem("Mode.Pause", pausing);
1836 if (appData.showButtonBar) {
1837 /* Always toggle, don't set. Previous code messes up when
1838 invoked while the button is pressed, as releasing it
1839 toggles the state again. */
1842 XtSetArg(args[0], XtNbackground, &oldbg);
1843 XtSetArg(args[1], XtNforeground, &oldfg);
1844 XtGetValues(optList[W_PAUSE].handle,
1846 XtSetArg(args[0], XtNbackground, oldfg);
1847 XtSetArg(args[1], XtNforeground, oldbg);
1849 XtSetValues(optList[W_PAUSE].handle, args, 2);
1853 wname = ModeToWidgetName(oldmode);
1854 if (wname != NULL) {
1855 MarkMenuItem(wname, False);
1857 wname = ModeToWidgetName(gameMode);
1858 if (wname != NULL) {
1859 MarkMenuItem(wname, True);
1862 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1864 /* Maybe all the enables should be handled here, not just this one */
1865 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1867 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1872 * Button/menu procedures
1875 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1876 char *selected_fen_position=NULL;
1879 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1880 Atom *type_return, XtPointer *value_return,
1881 unsigned long *length_return, int *format_return)
1883 char *selection_tmp;
1885 // if (!selected_fen_position) return False; /* should never happen */
1886 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1887 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1888 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1891 if (f == NULL) return False;
1895 selection_tmp = XtMalloc(len + 1);
1896 count = fread(selection_tmp, 1, len, f);
1899 XtFree(selection_tmp);
1902 selection_tmp[len] = NULLCHAR;
1904 /* note: since no XtSelectionDoneProc was registered, Xt will
1905 * automatically call XtFree on the value returned. So have to
1906 * make a copy of it allocated with XtMalloc */
1907 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1908 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1911 *value_return=selection_tmp;
1912 *length_return=strlen(selection_tmp);
1913 *type_return=*target;
1914 *format_return = 8; /* bits per byte */
1916 } else if (*target == XA_TARGETS(xDisplay)) {
1917 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1918 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1919 targets_tmp[1] = XA_STRING;
1920 *value_return = targets_tmp;
1921 *type_return = XA_ATOM;
1924 // This code leads to a read of value_return out of bounds on 64-bit systems.
1925 // Other code which I have seen always sets *format_return to 32 independent of
1926 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1927 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1928 *format_return = 8 * sizeof(Atom);
1929 if (*format_return > 32) {
1930 *length_return *= *format_return / 32;
1931 *format_return = 32;
1934 *format_return = 32;
1942 /* note: when called from menu all parameters are NULL, so no clue what the
1943 * Widget which was clicked on was, or what the click event was
1946 CopySomething (char *src)
1948 selected_fen_position = src;
1950 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
1951 * have a notion of a position that is selected but not copied.
1952 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
1954 XtOwnSelection(menuBarWidget, XA_PRIMARY,
1956 SendPositionSelection,
1957 NULL/* lose_ownership_proc */ ,
1958 NULL/* transfer_done_proc */);
1959 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
1961 SendPositionSelection,
1962 NULL/* lose_ownership_proc */ ,
1963 NULL/* transfer_done_proc */);
1966 /* function called when the data to Paste is ready */
1968 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
1969 Atom *type, XtPointer value, unsigned long *len, int *format)
1972 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
1973 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
1974 EditPositionPasteFEN(fenstr);
1978 /* called when Paste Position button is pressed,
1979 * all parameters will be NULL */
1981 PastePositionProc ()
1983 XtGetSelectionValue(menuBarWidget,
1984 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
1985 /* (XtSelectionCallbackProc) */ PastePositionCB,
1986 NULL, /* client_data passed to PastePositionCB */
1988 /* better to use the time field from the event that triggered the
1989 * call to this function, but that isn't trivial to get
1996 /* note: when called from menu all parameters are NULL, so no clue what the
1997 * Widget which was clicked on was, or what the click event was
1999 /* function called when the data to Paste is ready */
2001 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2002 Atom *type, XtPointer value, unsigned long *len, int *format)
2005 if (value == NULL || *len == 0) {
2006 return; /* nothing had been selected to copy */
2008 f = fopen(gamePasteFilename, "w");
2010 DisplayError(_("Can't open temp file"), errno);
2013 fwrite(value, 1, *len, f);
2016 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2019 /* called when Paste Game button is pressed,
2020 * all parameters will be NULL */
2024 XtGetSelectionValue(menuBarWidget,
2025 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2026 /* (XtSelectionCallbackProc) */ PasteGameCB,
2027 NULL, /* client_data passed to PasteGameCB */
2029 /* better to use the time field from the event that triggered the
2030 * call to this function, but that isn't trivial to get
2039 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2046 { // bassic primitive for determining if modifier keys are pressed
2047 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2050 XQueryKeymap(xDisplay,keys);
2051 for(i=0; i<6; i++) {
2053 j = XKeysymToKeycode(xDisplay, codes[i]);
2054 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2060 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2064 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2065 if ( n == 1 && *buf >= 32 // printable
2066 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2067 ) BoxAutoPopUp (buf);
2071 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2072 { // [HGM] input: let up-arrow recall previous line from history
2077 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2078 { // [HGM] input: let down-arrow recall next line from history
2083 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2089 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2091 if (!TempBackwardActive) {
2092 TempBackwardActive = True;
2098 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2100 /* Check to see if triggered by a key release event for a repeating key.
2101 * If so the next queued event will be a key press of the same key at the same time */
2102 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2104 XPeekEvent(xDisplay, &next);
2105 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2106 next.xkey.keycode == event->xkey.keycode)
2110 TempBackwardActive = False;
2114 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2115 { // called as key binding
2118 if (nprms && *nprms > 0)
2122 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2128 { // called from menu
2129 ManInner(NULL, NULL, NULL, NULL);
2133 SetWindowTitle (char *text, char *title, char *icon)
2137 if (appData.titleInWindow) {
2139 XtSetArg(args[i], XtNlabel, text); i++;
2140 XtSetValues(titleWidget, args, i);
2143 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2144 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2145 XtSetValues(shellWidget, args, i);
2146 XSync(xDisplay, False);
2151 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2157 DisplayIcsInteractionTitle (String message)
2159 if (oldICSInteractionTitle == NULL) {
2160 /* Magic to find the old window title, adapted from vim */
2161 char *wina = getenv("WINDOWID");
2163 Window win = (Window) atoi(wina);
2164 Window root, parent, *children;
2165 unsigned int nchildren;
2166 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2168 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2169 if (!XQueryTree(xDisplay, win, &root, &parent,
2170 &children, &nchildren)) break;
2171 if (children) XFree((void *)children);
2172 if (parent == root || parent == 0) break;
2175 XSetErrorHandler(oldHandler);
2177 if (oldICSInteractionTitle == NULL) {
2178 oldICSInteractionTitle = "xterm";
2181 printf("\033]0;%s\007", message);
2186 XtIntervalId delayedEventTimerXID = 0;
2187 DelayedEventCallback delayedEventCallback = 0;
2192 delayedEventTimerXID = 0;
2193 delayedEventCallback();
2197 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2199 if(delayedEventTimerXID && delayedEventCallback == cb)
2200 // [HGM] alive: replace, rather than add or flush identical event
2201 XtRemoveTimeOut(delayedEventTimerXID);
2202 delayedEventCallback = cb;
2203 delayedEventTimerXID =
2204 XtAppAddTimeOut(appContext, millisec,
2205 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2208 DelayedEventCallback
2211 if (delayedEventTimerXID) {
2212 return delayedEventCallback;
2219 CancelDelayedEvent ()
2221 if (delayedEventTimerXID) {
2222 XtRemoveTimeOut(delayedEventTimerXID);
2223 delayedEventTimerXID = 0;
2227 XtIntervalId loadGameTimerXID = 0;
2230 LoadGameTimerRunning ()
2232 return loadGameTimerXID != 0;
2236 StopLoadGameTimer ()
2238 if (loadGameTimerXID != 0) {
2239 XtRemoveTimeOut(loadGameTimerXID);
2240 loadGameTimerXID = 0;
2248 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2250 loadGameTimerXID = 0;
2255 StartLoadGameTimer (long millisec)
2258 XtAppAddTimeOut(appContext, millisec,
2259 (XtTimerCallbackProc) LoadGameTimerCallback,
2263 XtIntervalId analysisClockXID = 0;
2266 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2268 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2269 || appData.icsEngineAnalyze) { // [DM]
2270 AnalysisPeriodicEvent(0);
2271 StartAnalysisClock();
2276 StartAnalysisClock ()
2279 XtAppAddTimeOut(appContext, 2000,
2280 (XtTimerCallbackProc) AnalysisClockCallback,
2284 XtIntervalId clockTimerXID = 0;
2287 ClockTimerRunning ()
2289 return clockTimerXID != 0;
2295 if (clockTimerXID != 0) {
2296 XtRemoveTimeOut(clockTimerXID);
2305 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2312 StartClockTimer (long millisec)
2315 XtAppAddTimeOut(appContext, millisec,
2316 (XtTimerCallbackProc) ClockTimerCallback,
2321 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2325 Widget w = (Widget) opt->handle;
2327 /* check for low time warning */
2328 Pixel foregroundOrWarningColor = timerForegroundPixel;
2331 appData.lowTimeWarning &&
2332 (timer / 1000) < appData.icsAlarmTime)
2333 foregroundOrWarningColor = lowTimeWarningColor;
2335 if (appData.clockMode) {
2336 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2337 XtSetArg(args[0], XtNlabel, buf);
2339 snprintf(buf, MSG_SIZ, "%s ", color);
2340 XtSetArg(args[0], XtNlabel, buf);
2345 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2346 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2348 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2349 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2352 XtSetValues(w, args, 3);
2355 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2358 SetClockIcon (int color)
2361 Pixmap pm = *clockIcons[color];
2362 if (iconPixmap != pm) {
2364 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2365 XtSetValues(shellWidget, args, 1);
2369 #define INPUT_SOURCE_BUF_SIZE 8192
2378 char buf[INPUT_SOURCE_BUF_SIZE];
2383 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2385 InputSource *is = (InputSource *) closure;
2390 if (is->lineByLine) {
2391 count = read(is->fd, is->unused,
2392 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2394 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2397 is->unused += count;
2399 while (p < is->unused) {
2400 q = memchr(p, '\n', is->unused - p);
2401 if (q == NULL) break;
2403 (is->func)(is, is->closure, p, q - p, 0);
2407 while (p < is->unused) {
2412 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2417 (is->func)(is, is->closure, is->buf, count, error);
2422 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2425 ChildProc *cp = (ChildProc *) pr;
2427 is = (InputSource *) calloc(1, sizeof(InputSource));
2428 is->lineByLine = lineByLine;
2432 is->fd = fileno(stdin);
2434 is->kind = cp->kind;
2435 is->fd = cp->fdFrom;
2438 is->unused = is->buf;
2441 is->xid = XtAppAddInput(appContext, is->fd,
2442 (XtPointer) (XtInputReadMask),
2443 (XtInputCallbackProc) DoInputCallback,
2445 is->closure = closure;
2446 return (InputSourceRef) is;
2450 RemoveInputSource (InputSourceRef isr)
2452 InputSource *is = (InputSource *) isr;
2454 if (is->xid == 0) return;
2455 XtRemoveInput(is->xid);
2461 static Boolean frameWaiting;
2464 FrameAlarm (int sig)
2466 frameWaiting = False;
2467 /* In case System-V style signals. Needed?? */
2468 signal(SIGALRM, FrameAlarm);
2472 FrameDelay (int time)
2474 struct itimerval delay;
2476 XSync(xDisplay, False);
2479 frameWaiting = True;
2480 signal(SIGALRM, FrameAlarm);
2481 delay.it_interval.tv_sec =
2482 delay.it_value.tv_sec = time / 1000;
2483 delay.it_interval.tv_usec =
2484 delay.it_value.tv_usec = (time % 1000) * 1000;
2485 setitimer(ITIMER_REAL, &delay, NULL);
2486 while (frameWaiting) pause();
2487 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2488 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2489 setitimer(ITIMER_REAL, &delay, NULL);
2496 FrameDelay (int time)
2498 XSync(xDisplay, False);
2500 usleep(time * 1000);
2506 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2508 char buf[MSG_SIZ], *logoName = buf;
2509 if(appData.logo[n][0]) {
2510 logoName = appData.logo[n];
2511 } else if(appData.autoLogo) {
2512 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2513 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2514 } else if(appData.directory[n] && appData.directory[n][0]) {
2515 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2519 { ASSIGN(cps->programLogo, logoName); }
2523 UpdateLogos (int displ)
2525 if(optList[W_WHITE-1].handle == NULL) return;
2526 LoadLogo(&first, 0, 0);
2527 LoadLogo(&second, 1, appData.icsActive);
2528 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);