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 SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
267 void update_ics_width P(());
268 int CopyMemoProc P(());
271 * XBoard depends on Xt R4 or higher
273 int xtVersion = XtSpecificationRelease;
278 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
279 highlightSquareColor, premoveHighlightColor; // used in graphics
280 Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
281 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
282 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
283 Option *optList; // contains all widgets of main window
285 XFontSet fontSet, clockFontSet;
288 XFontStruct *clockFontStruct;
290 Font coordFontID, countFontID;
291 XFontStruct *coordFontStruct, *countFontStruct;
292 XtAppContext appContext;
295 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
297 Position commentX = -1, commentY = -1;
298 Dimension commentW, commentH;
299 typedef unsigned int BoardSize;
301 Boolean chessProgram;
303 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
304 int smallLayout = 0, tinyLayout = 0,
305 marginW, marginH, // [HGM] for run-time resizing
306 fromX = -1, fromY = -1, toX, toY, commentUp = False,
307 errorExitStatus = -1, defaultLineGap;
308 Dimension textHeight;
309 Pixel timerForegroundPixel, timerBackgroundPixel;
310 Pixel buttonForegroundPixel, buttonBackgroundPixel;
311 char *chessDir, *programName, *programVersion;
312 Boolean alwaysOnTop = False;
313 char *icsTextMenuString;
315 char *firstChessProgramNames;
316 char *secondChessProgramNames;
318 WindowPlacement wpMain;
319 WindowPlacement wpConsole;
320 WindowPlacement wpComment;
321 WindowPlacement wpMoveHistory;
322 WindowPlacement wpEvalGraph;
323 WindowPlacement wpEngineOutput;
324 WindowPlacement wpGameList;
325 WindowPlacement wpTags;
328 /* This magic number is the number of intermediate frames used
329 in each half of the animation. For short moves it's reduced
330 by 1. The total number of frames will be factor * 2 + 1. */
333 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
340 DropMenuEnables dmEnables[] = {
357 XtResource clientResources[] = {
358 { "flashCount", "flashCount", XtRInt, sizeof(int),
359 XtOffset(AppDataPtr, flashCount), XtRImmediate,
360 (XtPointer) FLASH_COUNT },
363 XrmOptionDescRec shellOptions[] = {
364 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
365 { "-flash", "flashCount", XrmoptionNoArg, "3" },
366 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
369 XtActionsRec boardActions[] = {
370 { "DrawPosition", DrawPositionProc },
371 { "HandlePV", HandlePV },
372 { "SelectPV", SelectPV },
373 { "StopPV", StopPV },
374 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
375 { "QuitProc", QuitWrapper },
376 { "ManProc", ManInner },
377 { "TempBackwardProc", TempBackwardProc },
378 { "TempForwardProc", TempForwardProc },
379 { "CommentClick", (XtActionProc) CommentClick },
380 { "GenericPopDown", (XtActionProc) GenericPopDown },
381 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
382 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
383 { "SelectMove", (XtActionProc) SelectMove },
384 { "LoadSelectedProc", LoadSelectedProc },
385 { "SetFilterProc", SetFilterProc },
386 { "TypeInProc", TypeInProc },
387 { "EnterKeyProc", EnterKeyProc },
388 { "UpKeyProc", UpKeyProc },
389 { "DownKeyProc", DownKeyProc },
390 { "WheelProc", WheelProc },
391 { "TabProc", TabProc },
394 char globalTranslations[] =
395 ":<Key>F9: MenuItem(Actions.Resign) \n \
396 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
397 :Meta<Key>V: MenuItem(File.NewVariant) \n \
398 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
399 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
400 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
401 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
402 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
403 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
404 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
405 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
406 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
407 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
408 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
409 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
410 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
411 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
412 :Ctrl<Key>q: MenuItem(File.Quit) \n \
413 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
414 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
415 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
416 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
417 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
418 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
419 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
420 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
421 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
422 :Meta<Key>G: MenuItem(View.GameList) \n \
423 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
424 :<Key>Pause: MenuItem(Mode.Pause) \n \
425 :<Key>F3: MenuItem(Action.Accept) \n \
426 :<Key>F4: MenuItem(Action.Decline) \n \
427 :<Key>F12: MenuItem(Action.Rematch) \n \
428 :<Key>F5: MenuItem(Action.CallFlag) \n \
429 :<Key>F6: MenuItem(Action.Draw) \n \
430 :<Key>F7: MenuItem(Action.Adjourn) \n \
431 :<Key>F8: MenuItem(Action.Abort) \n \
432 :<Key>F10: MenuItem(Action.StopObserving) \n \
433 :<Key>F11: MenuItem(Action.StopExamining) \n \
434 :Ctrl<Key>d: MenuItem(DebugProc) \n \
435 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
436 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
437 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
438 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
439 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
440 :<Key>Left: MenuItem(Edit.Backward) \n \
441 :<Key>Right: MenuItem(Edit.Forward) \n \
442 :<Key>Home: MenuItem(Edit.Revert) \n \
443 :<Key>End: MenuItem(Edit.TruncateGame) \n \
444 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
445 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
446 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
447 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
448 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
449 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
450 #ifndef OPTIONSDIALOG
452 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
453 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
454 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
455 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
456 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
459 :<Key>F1: MenuItem(Help.ManXBoard) \n \
460 :<Key>F2: MenuItem(View.FlipView) \n \
461 :<KeyDown>Return: TempBackwardProc() \n \
462 :<KeyUp>Return: TempForwardProc() \n";
464 char ICSInputTranslations[] =
465 "<Key>Up: UpKeyProc() \n "
466 "<Key>Down: DownKeyProc() \n "
467 "<Key>Return: EnterKeyProc() \n";
469 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
470 // as the widget is destroyed before the up-click can call extend-end
471 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
473 String xboardResources[] = {
474 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
479 /* Max possible square size */
480 #define MAXSQSIZE 256
482 static int xpm_avail[MAXSQSIZE];
484 #ifdef HAVE_DIR_STRUCT
486 /* Extract piece size from filename */
488 xpm_getsize (char *name, int len, char *ext)
496 if ((p=strchr(name, '.')) == NULL ||
497 StrCaseCmp(p+1, ext) != 0)
503 while (*p && isdigit(*p))
510 /* Setup xpm_avail */
512 xpm_getavail (char *dirname, char *ext)
518 for (i=0; i<MAXSQSIZE; ++i)
521 if (appData.debugMode)
522 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
524 dir = opendir(dirname);
527 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
528 programName, dirname);
532 while ((ent=readdir(dir)) != NULL) {
533 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
534 if (i > 0 && i < MAXSQSIZE)
544 xpm_print_avail (FILE *fp, char *ext)
548 fprintf(fp, _("Available `%s' sizes:\n"), ext);
549 for (i=1; i<MAXSQSIZE; ++i) {
555 /* Return XPM piecesize closest to size */
557 xpm_closest_to (char *dirname, int size, char *ext)
560 int sm_diff = MAXSQSIZE;
564 xpm_getavail(dirname, ext);
566 if (appData.debugMode)
567 xpm_print_avail(stderr, ext);
569 for (i=1; i<MAXSQSIZE; ++i) {
572 diff = (diff<0) ? -diff : diff;
573 if (diff < sm_diff) {
581 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
587 #else /* !HAVE_DIR_STRUCT */
588 /* If we are on a system without a DIR struct, we can't
589 read the directory, so we can't collect a list of
590 filenames, etc., so we can't do any size-fitting. */
592 xpm_closest_to (char *dirname, int size, char *ext)
595 Warning: No DIR structure found on this system --\n\
596 Unable to autosize for XPM/XIM pieces.\n\
597 Please report this error to %s.\n\
598 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
601 #endif /* HAVE_DIR_STRUCT */
604 /* Arrange to catch delete-window events */
605 Atom wm_delete_window;
607 CatchDeleteWindow (Widget w, String procname)
610 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
611 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
612 XtAugmentTranslations(w, XtParseTranslationTable(buf));
619 XtSetArg(args[0], XtNiconic, False);
620 XtSetValues(shellWidget, args, 1);
622 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
625 //---------------------------------------------------------------------------------------------------------
626 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
629 #define CW_USEDEFAULT (1<<31)
630 #define ICS_TEXT_MENU_SIZE 90
631 #define DEBUG_FILE "xboard.debug"
632 #define SetCurrentDirectory chdir
633 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
637 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
640 // front-end part of option handling
642 // [HGM] This platform-dependent table provides the location for storing the color info
643 extern char *crWhite, * crBlack;
647 &appData.whitePieceColor,
648 &appData.blackPieceColor,
649 &appData.lightSquareColor,
650 &appData.darkSquareColor,
651 &appData.highlightSquareColor,
652 &appData.premoveHighlightColor,
653 &appData.lowTimeWarningColor,
664 // [HGM] font: keep a font for each square size, even non-stndard ones
667 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
668 char *fontTable[NUM_FONTS][MAX_SIZE];
671 ParseFont (char *name, int number)
672 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
674 if(sscanf(name, "size%d:", &size)) {
675 // [HGM] font: font is meant for specific boardSize (likely from settings file);
676 // defer processing it until we know if it matches our board size
677 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
678 fontTable[number][size] = strdup(strchr(name, ':')+1);
679 fontValid[number][size] = True;
684 case 0: // CLOCK_FONT
685 appData.clockFont = strdup(name);
687 case 1: // MESSAGE_FONT
688 appData.font = strdup(name);
690 case 2: // COORD_FONT
691 appData.coordFont = strdup(name);
696 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
701 { // only 2 fonts currently
702 appData.clockFont = CLOCK_FONT_NAME;
703 appData.coordFont = COORD_FONT_NAME;
704 appData.font = DEFAULT_FONT_NAME;
709 { // no-op, until we identify the code for this already in XBoard and move it here
713 ParseColor (int n, char *name)
714 { // in XBoard, just copy the color-name string
715 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
719 ParseTextAttribs (ColorClass cc, char *s)
721 (&appData.colorShout)[cc] = strdup(s);
725 ParseBoardSize (void *addr, char *name)
727 appData.boardSize = strdup(name);
732 { // In XBoard the sound-playing program takes care of obtaining the actual sound
736 SetCommPortDefaults ()
737 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
740 // [HGM] args: these three cases taken out to stay in front-end
742 SaveFontArg (FILE *f, ArgDescriptor *ad)
745 int i, n = (int)(intptr_t)ad->argLoc;
747 case 0: // CLOCK_FONT
748 name = appData.clockFont;
750 case 1: // MESSAGE_FONT
753 case 2: // COORD_FONT
754 name = appData.coordFont;
759 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
760 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
761 fontTable[n][squareSize] = strdup(name);
762 fontValid[n][squareSize] = True;
765 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
766 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
771 { // nothing to do, as the sounds are at all times represented by their text-string names already
775 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
776 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
777 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
781 SaveColor (FILE *f, ArgDescriptor *ad)
782 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
783 if(colorVariable[(int)(intptr_t)ad->argLoc])
784 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
788 SaveBoardSize (FILE *f, char *name, void *addr)
789 { // wrapper to shield back-end from BoardSize & sizeInfo
790 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
794 ParseCommPortSettings (char *s)
795 { // no such option in XBoard (yet)
801 GetActualPlacement (Widget wg, WindowPlacement *wp)
803 XWindowAttributes winAt;
810 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
811 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
812 wp->x = rx - winAt.x;
813 wp->y = ry - winAt.y;
814 wp->height = winAt.height;
815 wp->width = winAt.width;
816 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
821 { // wrapper to shield use of window handles from back-end (make addressible by number?)
822 // In XBoard this will have to wait until awareness of window parameters is implemented
823 GetActualPlacement(shellWidget, &wpMain);
824 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
825 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
826 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
827 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
828 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
829 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
833 PrintCommPortSettings (FILE *f, char *name)
834 { // This option does not exist in XBoard
838 EnsureOnScreen (int *x, int *y, int minX, int minY)
845 { // [HGM] args: allows testing if main window is realized from back-end
846 return xBoardWindow != 0;
850 PopUpStartupDialog ()
851 { // start menu not implemented in XBoard
855 ConvertToLine (int argc, char **argv)
857 static char line[128*1024], buf[1024];
861 for(i=1; i<argc; i++)
863 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
864 && argv[i][0] != '{' )
865 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
867 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
868 strncat(line, buf, 128*1024 - strlen(line) - 1 );
871 line[strlen(line)-1] = NULLCHAR;
875 //--------------------------------------------------------------------------------------------
878 ResizeBoardWindow (int w, int h, int inhibit)
880 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
882 shellArgs[0].value = w;
883 shellArgs[1].value = h;
884 shellArgs[4].value = shellArgs[2].value = w;
885 shellArgs[5].value = shellArgs[3].value = h;
886 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
888 XSync(xDisplay, False);
892 MakeOneColor (char *name, Pixel *color)
895 if (!appData.monoMode) {
896 vFrom.addr = (caddr_t) name;
897 vFrom.size = strlen(name);
898 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
899 if (vTo.addr == NULL) {
900 appData.monoMode = True;
903 *color = *(Pixel *) vTo.addr;
911 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
912 int forceMono = False;
914 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
915 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
916 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
917 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
918 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
919 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
920 if (appData.lowTimeWarning)
921 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
922 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
923 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
929 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
930 { // detervtomine what fonts to use, and create them
934 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
935 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
936 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
937 appData.font = fontTable[MESSAGE_FONT][squareSize];
938 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
939 appData.coordFont = fontTable[COORD_FONT][squareSize];
942 appData.font = InsertPxlSize(appData.font, fontPxlSize);
943 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
944 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
945 fontSet = CreateFontSet(appData.font);
946 clockFontSet = CreateFontSet(appData.clockFont);
948 /* For the coordFont, use the 0th font of the fontset. */
949 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
950 XFontStruct **font_struct_list;
951 XFontSetExtents *fontSize;
952 char **font_name_list;
953 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
954 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
955 coordFontStruct = XQueryFont(xDisplay, coordFontID);
956 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
957 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
960 appData.font = FindFont(appData.font, fontPxlSize);
961 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
962 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
963 clockFontID = XLoadFont(xDisplay, appData.clockFont);
964 clockFontStruct = XQueryFont(xDisplay, clockFontID);
965 coordFontID = XLoadFont(xDisplay, appData.coordFont);
966 coordFontStruct = XQueryFont(xDisplay, coordFontID);
967 // textHeight in !NLS mode!
969 countFontID = coordFontID; // [HGM] holdings
970 countFontStruct = coordFontStruct;
972 xdb = XtDatabase(xDisplay);
974 XrmPutLineResource(&xdb, "*international: True");
975 vTo.size = sizeof(XFontSet);
976 vTo.addr = (XtPointer) &fontSet;
977 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
979 XrmPutStringResource(&xdb, "*font", appData.font);
989 case ArgInt: p = " N"; break;
990 case ArgString: p = " STR"; break;
991 case ArgBoolean: p = " TF"; break;
992 case ArgSettingsFilename:
993 case ArgFilename: p = " FILE"; break;
994 case ArgX: p = " Nx"; break;
995 case ArgY: p = " Ny"; break;
996 case ArgAttribs: p = " TEXTCOL"; break;
997 case ArgColor: p = " COL"; break;
998 case ArgFont: p = " FONT"; break;
999 case ArgBoardSize: p = " SIZE"; break;
1000 case ArgFloat: p = " FLOAT"; break;
1005 case ArgCommSettings:
1016 ArgDescriptor *q, *p = argDescriptors+5;
1017 printf("\nXBoard accepts the following options:\n"
1018 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1019 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1020 " SIZE = board-size spec(s)\n"
1021 " Within parentheses are short forms, or options to set to true or false.\n"
1022 " Persistent options (saved in the settings file) are marked with *)\n\n");
1024 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1025 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1026 if(p->save) strcat(buf+len, "*");
1027 for(q=p+1; q->argLoc == p->argLoc; q++) {
1028 if(q->argName[0] == '-') continue;
1029 strcat(buf+len, q == p+1 ? " (" : " ");
1030 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1032 if(q != p+1) strcat(buf+len, ")");
1034 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1037 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1041 main (int argc, char **argv)
1043 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1044 XSetWindowAttributes window_attributes;
1046 Dimension boardWidth, boardHeight, w, h;
1048 int forceMono = False;
1050 srandom(time(0)); // [HGM] book: make random truly random
1052 setbuf(stdout, NULL);
1053 setbuf(stderr, NULL);
1056 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1057 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1061 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1066 programName = strrchr(argv[0], '/');
1067 if (programName == NULL)
1068 programName = argv[0];
1073 XtSetLanguageProc(NULL, NULL, NULL);
1074 if (appData.debugMode) {
1075 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1078 bindtextdomain(PACKAGE, LOCALEDIR);
1079 textdomain(PACKAGE);
1082 appData.boardSize = "";
1083 InitAppData(ConvertToLine(argc, argv));
1085 if (p == NULL) p = "/tmp";
1086 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1087 gameCopyFilename = (char*) malloc(i);
1088 gamePasteFilename = (char*) malloc(i);
1089 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1090 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1092 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1093 static char buf[MSG_SIZ];
1094 EscapeExpand(buf, appData.firstInitString);
1095 appData.firstInitString = strdup(buf);
1096 EscapeExpand(buf, appData.secondInitString);
1097 appData.secondInitString = strdup(buf);
1098 EscapeExpand(buf, appData.firstComputerString);
1099 appData.firstComputerString = strdup(buf);
1100 EscapeExpand(buf, appData.secondComputerString);
1101 appData.secondComputerString = strdup(buf);
1104 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1107 if (chdir(chessDir) != 0) {
1108 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1114 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1115 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1116 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1117 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1120 setbuf(debugFP, NULL);
1123 /* [HGM,HR] make sure board size is acceptable */
1124 if(appData.NrFiles > BOARD_FILES ||
1125 appData.NrRanks > BOARD_RANKS )
1126 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1129 /* This feature does not work; animation needs a rewrite */
1130 appData.highlightDragging = FALSE;
1134 gameInfo.variant = StringToVariant(appData.variant);
1135 InitPosition(FALSE);
1138 XtAppInitialize(&appContext, "XBoard", shellOptions,
1139 XtNumber(shellOptions),
1140 &argc, argv, xboardResources, NULL, 0);
1142 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1143 clientResources, XtNumber(clientResources),
1146 xDisplay = XtDisplay(shellWidget);
1147 xScreen = DefaultScreen(xDisplay);
1148 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1151 * determine size, based on supplied or remembered -size, or screen size
1153 if (isdigit(appData.boardSize[0])) {
1154 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1155 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1156 &fontPxlSize, &smallLayout, &tinyLayout);
1158 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1159 programName, appData.boardSize);
1163 /* Find some defaults; use the nearest known size */
1164 SizeDefaults *szd, *nearest;
1165 int distance = 99999;
1166 nearest = szd = sizeDefaults;
1167 while (szd->name != NULL) {
1168 if (abs(szd->squareSize - squareSize) < distance) {
1170 distance = abs(szd->squareSize - squareSize);
1171 if (distance == 0) break;
1175 if (i < 2) lineGap = nearest->lineGap;
1176 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1177 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1178 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1179 if (i < 6) smallLayout = nearest->smallLayout;
1180 if (i < 7) tinyLayout = nearest->tinyLayout;
1183 SizeDefaults *szd = sizeDefaults;
1184 if (*appData.boardSize == NULLCHAR) {
1185 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1186 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1189 if (szd->name == NULL) szd--;
1190 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1192 while (szd->name != NULL &&
1193 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1194 if (szd->name == NULL) {
1195 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1196 programName, appData.boardSize);
1200 squareSize = szd->squareSize;
1201 lineGap = szd->lineGap;
1202 clockFontPxlSize = szd->clockFontPxlSize;
1203 coordFontPxlSize = szd->coordFontPxlSize;
1204 fontPxlSize = szd->fontPxlSize;
1205 smallLayout = szd->smallLayout;
1206 tinyLayout = szd->tinyLayout;
1207 // [HGM] font: use defaults from settings file if available and not overruled
1210 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1211 if (strlen(appData.pixmapDirectory) > 0) {
1212 p = ExpandPathName(appData.pixmapDirectory);
1214 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1215 appData.pixmapDirectory);
1218 if (appData.debugMode) {
1219 fprintf(stderr, _("\
1220 XBoard square size (hint): %d\n\
1221 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1223 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1224 if (appData.debugMode) {
1225 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1228 defaultLineGap = lineGap;
1229 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1231 /* [HR] height treated separately (hacked) */
1232 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1233 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1236 * Determine what fonts to use.
1238 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1241 * Detect if there are not enough colors available and adapt.
1243 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1244 appData.monoMode = True;
1247 forceMono = MakeColors();
1250 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1252 appData.monoMode = True;
1255 if (appData.monoMode && appData.debugMode) {
1256 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1257 (unsigned long) XWhitePixel(xDisplay, xScreen),
1258 (unsigned long) XBlackPixel(xDisplay, xScreen));
1261 ParseIcsTextColors();
1263 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1269 layoutName = "tinyLayout";
1270 } else if (smallLayout) {
1271 layoutName = "smallLayout";
1273 layoutName = "normalLayout";
1276 optList = BoardPopUp(squareSize, lineGap, (void*)
1282 boardWidget = optList[W_BOARD].handle;
1283 menuBarWidget = optList[W_MENU].handle;
1284 dropMenu = optList[W_DROP].handle;
1285 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1286 formWidget = XtParent(boardWidget);
1287 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1288 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1289 XtGetValues(optList[W_WHITE].handle, args, 2);
1290 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1291 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1292 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1293 XtGetValues(optList[W_PAUSE].handle, args, 2);
1295 AppendEnginesToMenu(appData.recentEngineList);
1297 xBoardWindow = XtWindow(boardWidget);
1299 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1300 // not need to go into InitDrawingSizes().
1303 * Create X checkmark bitmap and initialize option menu checks.
1305 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1306 checkmark_bits, checkmark_width, checkmark_height);
1312 ReadBitmap(&wIconPixmap, "icon_white.bm",
1313 icon_white_bits, icon_white_width, icon_white_height);
1314 ReadBitmap(&bIconPixmap, "icon_black.bm",
1315 icon_black_bits, icon_black_width, icon_black_height);
1316 iconPixmap = wIconPixmap;
1318 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1319 XtSetValues(shellWidget, args, i);
1322 * Create a cursor for the board widget.
1324 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1325 XChangeWindowAttributes(xDisplay, xBoardWindow,
1326 CWCursor, &window_attributes);
1329 * Inhibit shell resizing.
1331 shellArgs[0].value = (XtArgVal) &w;
1332 shellArgs[1].value = (XtArgVal) &h;
1333 XtGetValues(shellWidget, shellArgs, 2);
1334 shellArgs[4].value = shellArgs[2].value = w;
1335 shellArgs[5].value = shellArgs[3].value = h;
1336 if(!CreateAnyPieces()) XtSetValues(shellWidget, &shellArgs[2], 4);
1337 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1338 marginH = h - boardHeight;
1340 CatchDeleteWindow(shellWidget, "QuitProc");
1345 if(appData.logoSize)
1346 { // locate and read user logo
1348 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1349 ASSIGN(userLogo, buf);
1352 if (appData.animate || appData.animateDragging)
1355 XtAugmentTranslations(formWidget,
1356 XtParseTranslationTable(globalTranslations));
1358 XtAddEventHandler(formWidget, KeyPressMask, False,
1359 (XtEventHandler) MoveTypeInProc, NULL);
1360 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1361 (XtEventHandler) EventProc, NULL);
1363 /* [AS] Restore layout */
1364 if( wpMoveHistory.visible ) {
1368 if( wpEvalGraph.visible )
1373 if( wpEngineOutput.visible ) {
1374 EngineOutputPopUp();
1379 if (errorExitStatus == -1) {
1380 if (appData.icsActive) {
1381 /* We now wait until we see "login:" from the ICS before
1382 sending the logon script (problems with timestamp otherwise) */
1383 /*ICSInitScript();*/
1384 if (appData.icsInputBox) ICSInputBoxPopUp();
1388 signal(SIGWINCH, TermSizeSigHandler);
1390 signal(SIGINT, IntSigHandler);
1391 signal(SIGTERM, IntSigHandler);
1392 if (*appData.cmailGameName != NULLCHAR) {
1393 signal(SIGUSR1, CmailSigHandler);
1397 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1400 // XtSetKeyboardFocus(shellWidget, formWidget);
1401 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1403 XtAppMainLoop(appContext);
1404 if (appData.debugMode) fclose(debugFP); // [DM] debug
1409 TermSizeSigHandler (int sig)
1415 IntSigHandler (int sig)
1421 CmailSigHandler (int sig)
1426 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1428 /* Activate call-back function CmailSigHandlerCallBack() */
1429 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1431 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1435 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1438 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1440 /**** end signal code ****/
1443 #define Abs(n) ((n)<0 ? -(n) : (n))
1447 InsertPxlSize (char *pattern, int targetPxlSize)
1449 char *base_fnt_lst, strInt[12], *p, *q;
1450 int alternatives, i, len, strIntLen;
1453 * Replace the "*" (if present) in the pixel-size slot of each
1454 * alternative with the targetPxlSize.
1458 while ((p = strchr(p, ',')) != NULL) {
1462 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1463 strIntLen = strlen(strInt);
1464 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1468 while (alternatives--) {
1469 char *comma = strchr(p, ',');
1470 for (i=0; i<14; i++) {
1471 char *hyphen = strchr(p, '-');
1473 if (comma && hyphen > comma) break;
1474 len = hyphen + 1 - p;
1475 if (i == 7 && *p == '*' && len == 2) {
1477 memcpy(q, strInt, strIntLen);
1487 len = comma + 1 - p;
1494 return base_fnt_lst;
1498 CreateFontSet (char *base_fnt_lst)
1501 char **missing_list;
1505 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1506 &missing_list, &missing_count, &def_string);
1507 if (appData.debugMode) {
1509 XFontStruct **font_struct_list;
1510 char **font_name_list;
1511 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1513 fprintf(debugFP, " got list %s, locale %s\n",
1514 XBaseFontNameListOfFontSet(fntSet),
1515 XLocaleOfFontSet(fntSet));
1516 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1517 for (i = 0; i < count; i++) {
1518 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1521 for (i = 0; i < missing_count; i++) {
1522 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1525 if (fntSet == NULL) {
1526 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1531 #else // not ENABLE_NLS
1533 * Find a font that matches "pattern" that is as close as
1534 * possible to the targetPxlSize. Prefer fonts that are k
1535 * pixels smaller to fonts that are k pixels larger. The
1536 * pattern must be in the X Consortium standard format,
1537 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1538 * The return value should be freed with XtFree when no
1542 FindFont (char *pattern, int targetPxlSize)
1544 char **fonts, *p, *best, *scalable, *scalableTail;
1545 int i, j, nfonts, minerr, err, pxlSize;
1547 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1549 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1550 programName, pattern);
1557 for (i=0; i<nfonts; i++) {
1560 if (*p != '-') continue;
1562 if (*p == NULLCHAR) break;
1563 if (*p++ == '-') j++;
1565 if (j < 7) continue;
1568 scalable = fonts[i];
1571 err = pxlSize - targetPxlSize;
1572 if (Abs(err) < Abs(minerr) ||
1573 (minerr > 0 && err < 0 && -err == minerr)) {
1579 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1580 /* If the error is too big and there is a scalable font,
1581 use the scalable font. */
1582 int headlen = scalableTail - scalable;
1583 p = (char *) XtMalloc(strlen(scalable) + 10);
1584 while (isdigit(*scalableTail)) scalableTail++;
1585 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1587 p = (char *) XtMalloc(strlen(best) + 2);
1588 safeStrCpy(p, best, strlen(best)+1 );
1590 if (appData.debugMode) {
1591 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1592 pattern, targetPxlSize, p);
1594 XFreeFontNames(fonts);
1600 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1605 char msg[MSG_SIZ], fullname[MSG_SIZ];
1607 if (*appData.bitmapDirectory != NULLCHAR) {
1608 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
1609 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
1610 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
1611 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
1612 &w, &h, pm, &x_hot, &y_hot);
1613 fprintf(stderr, "load %s\n", name);
1614 if (errcode != BitmapSuccess) {
1616 case BitmapOpenFailed:
1617 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
1619 case BitmapFileInvalid:
1620 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
1622 case BitmapNoMemory:
1623 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
1627 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
1631 fprintf(stderr, _("%s: %s...using built-in\n"),
1633 } else if (w != wreq || h != hreq) {
1635 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
1636 programName, fullname, w, h, wreq, hreq);
1642 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1648 MarkMenuItem (char *menuRef, int state)
1650 MenuItem *item = MenuNameToItem(menuRef);
1654 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1655 XtSetValues(item->handle, args, 1);
1660 EnableNamedMenuItem (char *menuRef, int state)
1662 MenuItem *item = MenuNameToItem(menuRef);
1664 if(item) XtSetSensitive(item->handle, state);
1668 EnableButtonBar (int state)
1670 XtSetSensitive(optList[W_BUTTON].handle, state);
1675 SetMenuEnables (Enables *enab)
1677 while (enab->name != NULL) {
1678 EnableNamedMenuItem(enab->name, enab->value);
1684 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1685 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1687 if(*nprms == 0) return;
1688 item = MenuNameToItem(prms[0]);
1689 if(item) ((MenuProc *) item->proc) ();
1693 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
1695 RecentEngineEvent((int) (intptr_t) addr);
1699 AppendMenuItem (char *msg, int n)
1701 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
1713 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1714 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1715 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1716 dmEnables[i].piece);
1717 XtSetSensitive(entry, p != NULL || !appData.testLegality
1718 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1719 && !appData.icsActive));
1721 while (p && *p++ == dmEnables[i].piece) count++;
1722 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1724 XtSetArg(args[j], XtNlabel, label); j++;
1725 XtSetValues(entry, args, j);
1731 do_flash_delay (unsigned long msec)
1737 FlashDelay (int flash_delay)
1739 XSync(xDisplay, False);
1740 if(flash_delay) do_flash_delay(flash_delay);
1744 Fraction (int x, int start, int stop)
1746 double f = ((double) x - start)/(stop - start);
1747 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1751 static WindowPlacement wpNew;
1754 CoDrag (Widget sh, WindowPlacement *wp)
1757 int j=0, touch=0, fudge = 2;
1758 GetActualPlacement(sh, wp);
1759 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1760 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1761 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1762 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1763 if(!touch ) return; // only windows that touch co-move
1764 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1765 int heightInc = wpNew.height - wpMain.height;
1766 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1767 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1768 wp->y += fracTop * heightInc;
1769 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1770 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1771 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1772 int widthInc = wpNew.width - wpMain.width;
1773 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1774 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1775 wp->y += fracLeft * widthInc;
1776 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1777 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1779 wp->x += wpNew.x - wpMain.x;
1780 wp->y += wpNew.y - wpMain.y;
1781 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1782 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1783 XtSetArg(args[j], XtNx, wp->x); j++;
1784 XtSetArg(args[j], XtNy, wp->y); j++;
1785 XtSetValues(sh, args, j);
1789 ReSize (WindowPlacement *wp)
1792 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1793 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1794 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1795 if(sqy < sqx) sqx = sqy;
1796 if(sqx != squareSize) {
1797 squareSize = sqx; // adopt new square size
1799 CreatePNGPieces(); // make newly scaled pieces
1800 InitDrawingSizes(0, 0); // creates grid etc.
1804 static XtIntervalId delayedDragID = 0;
1813 GetActualPlacement(shellWidget, &wpNew);
1814 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1815 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1816 busy = 0; return; // false alarm
1819 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1820 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1821 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1822 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1824 DrawPosition(True, NULL);
1825 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1833 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1835 XtAppAddTimeOut(appContext, 100, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1839 EventProc (Widget widget, caddr_t unused, XEvent *event)
1841 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1842 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1846 * event handler for redrawing the board
1849 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1851 DrawPosition(True, NULL);
1856 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1857 { // [HGM] pv: walk PV
1858 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1861 static int savedIndex; /* gross that this is global */
1864 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1867 XawTextPosition index, dummy;
1870 XawTextGetSelectionPos(w, &index, &dummy);
1871 XtSetArg(arg, XtNstring, &val);
1872 XtGetValues(w, &arg, 1);
1873 ReplaceComment(savedIndex, val);
1874 if(savedIndex != currentMove) ToNrEvent(savedIndex);
1875 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1879 EditCommentPopUp (int index, char *title, char *text)
1882 if (text == NULL) text = "";
1883 NewCommentPopup(title, text, index);
1887 CommentPopUp (char *title, char *text)
1889 savedIndex = currentMove; // [HGM] vari
1890 NewCommentPopup(title, text, currentMove);
1896 PopDown(CommentDlg);
1900 /* Disable all user input other than deleting the window */
1901 static int frozen = 0;
1907 /* Grab by a widget that doesn't accept input */
1908 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1912 /* Undo a FreezeUI */
1916 if (!frozen) return;
1917 XtRemoveGrab(optList[W_MESSG].handle);
1925 static int oldPausing = FALSE;
1926 static GameMode oldmode = (GameMode) -1;
1929 if (!boardWidget || !XtIsRealized(boardWidget)) return;
1931 if (pausing != oldPausing) {
1932 oldPausing = pausing;
1933 MarkMenuItem("Mode.Pause", pausing);
1935 if (appData.showButtonBar) {
1936 /* Always toggle, don't set. Previous code messes up when
1937 invoked while the button is pressed, as releasing it
1938 toggles the state again. */
1941 XtSetArg(args[0], XtNbackground, &oldbg);
1942 XtSetArg(args[1], XtNforeground, &oldfg);
1943 XtGetValues(optList[W_PAUSE].handle,
1945 XtSetArg(args[0], XtNbackground, oldfg);
1946 XtSetArg(args[1], XtNforeground, oldbg);
1948 XtSetValues(optList[W_PAUSE].handle, args, 2);
1952 wname = ModeToWidgetName(oldmode);
1953 if (wname != NULL) {
1954 MarkMenuItem(wname, False);
1956 wname = ModeToWidgetName(gameMode);
1957 if (wname != NULL) {
1958 MarkMenuItem(wname, True);
1961 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1963 /* Maybe all the enables should be handled here, not just this one */
1964 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1966 DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
1971 * Button/menu procedures
1974 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1975 char *selected_fen_position=NULL;
1978 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1979 Atom *type_return, XtPointer *value_return,
1980 unsigned long *length_return, int *format_return)
1982 char *selection_tmp;
1984 // if (!selected_fen_position) return False; /* should never happen */
1985 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1986 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1987 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1990 if (f == NULL) return False;
1994 selection_tmp = XtMalloc(len + 1);
1995 count = fread(selection_tmp, 1, len, f);
1998 XtFree(selection_tmp);
2001 selection_tmp[len] = NULLCHAR;
2003 /* note: since no XtSelectionDoneProc was registered, Xt will
2004 * automatically call XtFree on the value returned. So have to
2005 * make a copy of it allocated with XtMalloc */
2006 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
2007 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
2010 *value_return=selection_tmp;
2011 *length_return=strlen(selection_tmp);
2012 *type_return=*target;
2013 *format_return = 8; /* bits per byte */
2015 } else if (*target == XA_TARGETS(xDisplay)) {
2016 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
2017 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
2018 targets_tmp[1] = XA_STRING;
2019 *value_return = targets_tmp;
2020 *type_return = XA_ATOM;
2023 // This code leads to a read of value_return out of bounds on 64-bit systems.
2024 // Other code which I have seen always sets *format_return to 32 independent of
2025 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
2026 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
2027 *format_return = 8 * sizeof(Atom);
2028 if (*format_return > 32) {
2029 *length_return *= *format_return / 32;
2030 *format_return = 32;
2033 *format_return = 32;
2041 /* note: when called from menu all parameters are NULL, so no clue what the
2042 * Widget which was clicked on was, or what the click event was
2045 CopySomething (char *src)
2047 selected_fen_position = src;
2049 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2050 * have a notion of a position that is selected but not copied.
2051 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2053 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2055 SendPositionSelection,
2056 NULL/* lose_ownership_proc */ ,
2057 NULL/* transfer_done_proc */);
2058 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2060 SendPositionSelection,
2061 NULL/* lose_ownership_proc */ ,
2062 NULL/* transfer_done_proc */);
2065 /* function called when the data to Paste is ready */
2067 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2068 Atom *type, XtPointer value, unsigned long *len, int *format)
2071 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2072 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2073 EditPositionPasteFEN(fenstr);
2077 /* called when Paste Position button is pressed,
2078 * all parameters will be NULL */
2080 PastePositionProc ()
2082 XtGetSelectionValue(menuBarWidget,
2083 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2084 /* (XtSelectionCallbackProc) */ PastePositionCB,
2085 NULL, /* client_data passed to PastePositionCB */
2087 /* better to use the time field from the event that triggered the
2088 * call to this function, but that isn't trivial to get
2095 /* note: when called from menu all parameters are NULL, so no clue what the
2096 * Widget which was clicked on was, or what the click event was
2098 /* function called when the data to Paste is ready */
2100 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2101 Atom *type, XtPointer value, unsigned long *len, int *format)
2104 if (value == NULL || *len == 0) {
2105 return; /* nothing had been selected to copy */
2107 f = fopen(gamePasteFilename, "w");
2109 DisplayError(_("Can't open temp file"), errno);
2112 fwrite(value, 1, *len, f);
2115 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2118 /* called when Paste Game button is pressed,
2119 * all parameters will be NULL */
2123 XtGetSelectionValue(menuBarWidget,
2124 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2125 /* (XtSelectionCallbackProc) */ PasteGameCB,
2126 NULL, /* client_data passed to PasteGameCB */
2128 /* better to use the time field from the event that triggered the
2129 * call to this function, but that isn't trivial to get
2138 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2145 { // bassic primitive for determining if modifier keys are pressed
2146 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2149 XQueryKeymap(xDisplay,keys);
2150 for(i=0; i<6; i++) {
2152 j = XKeysymToKeycode(xDisplay, codes[i]);
2153 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2159 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2163 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2164 if ( n == 1 && *buf >= 32 // printable
2165 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2166 ) BoxAutoPopUp (buf);
2170 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2171 { // [HGM] input: let up-arrow recall previous line from history
2176 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2177 { // [HGM] input: let down-arrow recall next line from history
2182 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2188 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2190 if (!TempBackwardActive) {
2191 TempBackwardActive = True;
2197 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2199 /* Check to see if triggered by a key release event for a repeating key.
2200 * If so the next queued event will be a key press of the same key at the same time */
2201 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2203 XPeekEvent(xDisplay, &next);
2204 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2205 next.xkey.keycode == event->xkey.keycode)
2209 TempBackwardActive = False;
2213 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2214 { // called as key binding
2217 if (nprms && *nprms > 0)
2221 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2227 { // called from menu
2228 ManInner(NULL, NULL, NULL, NULL);
2232 SetWindowTitle (char *text, char *title, char *icon)
2236 if (appData.titleInWindow) {
2238 XtSetArg(args[i], XtNlabel, text); i++;
2239 XtSetValues(titleWidget, args, i);
2242 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2243 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2244 XtSetValues(shellWidget, args, i);
2245 XSync(xDisplay, False);
2250 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2256 DisplayIcsInteractionTitle (String message)
2258 if (oldICSInteractionTitle == NULL) {
2259 /* Magic to find the old window title, adapted from vim */
2260 char *wina = getenv("WINDOWID");
2262 Window win = (Window) atoi(wina);
2263 Window root, parent, *children;
2264 unsigned int nchildren;
2265 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2267 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2268 if (!XQueryTree(xDisplay, win, &root, &parent,
2269 &children, &nchildren)) break;
2270 if (children) XFree((void *)children);
2271 if (parent == root || parent == 0) break;
2274 XSetErrorHandler(oldHandler);
2276 if (oldICSInteractionTitle == NULL) {
2277 oldICSInteractionTitle = "xterm";
2280 printf("\033]0;%s\007", message);
2285 XtIntervalId delayedEventTimerXID = 0;
2286 DelayedEventCallback delayedEventCallback = 0;
2291 delayedEventTimerXID = 0;
2292 delayedEventCallback();
2296 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2298 if(delayedEventTimerXID && delayedEventCallback == cb)
2299 // [HGM] alive: replace, rather than add or flush identical event
2300 XtRemoveTimeOut(delayedEventTimerXID);
2301 delayedEventCallback = cb;
2302 delayedEventTimerXID =
2303 XtAppAddTimeOut(appContext, millisec,
2304 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2307 DelayedEventCallback
2310 if (delayedEventTimerXID) {
2311 return delayedEventCallback;
2318 CancelDelayedEvent ()
2320 if (delayedEventTimerXID) {
2321 XtRemoveTimeOut(delayedEventTimerXID);
2322 delayedEventTimerXID = 0;
2326 XtIntervalId loadGameTimerXID = 0;
2329 LoadGameTimerRunning ()
2331 return loadGameTimerXID != 0;
2335 StopLoadGameTimer ()
2337 if (loadGameTimerXID != 0) {
2338 XtRemoveTimeOut(loadGameTimerXID);
2339 loadGameTimerXID = 0;
2347 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2349 loadGameTimerXID = 0;
2354 StartLoadGameTimer (long millisec)
2357 XtAppAddTimeOut(appContext, millisec,
2358 (XtTimerCallbackProc) LoadGameTimerCallback,
2362 XtIntervalId analysisClockXID = 0;
2365 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2367 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2368 || appData.icsEngineAnalyze) { // [DM]
2369 AnalysisPeriodicEvent(0);
2370 StartAnalysisClock();
2375 StartAnalysisClock ()
2378 XtAppAddTimeOut(appContext, 2000,
2379 (XtTimerCallbackProc) AnalysisClockCallback,
2383 XtIntervalId clockTimerXID = 0;
2386 ClockTimerRunning ()
2388 return clockTimerXID != 0;
2394 if (clockTimerXID != 0) {
2395 XtRemoveTimeOut(clockTimerXID);
2404 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2411 StartClockTimer (long millisec)
2414 XtAppAddTimeOut(appContext, millisec,
2415 (XtTimerCallbackProc) ClockTimerCallback,
2420 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2424 Widget w = (Widget) opt->handle;
2426 /* check for low time warning */
2427 Pixel foregroundOrWarningColor = timerForegroundPixel;
2430 appData.lowTimeWarning &&
2431 (timer / 1000) < appData.icsAlarmTime)
2432 foregroundOrWarningColor = lowTimeWarningColor;
2434 if (appData.clockMode) {
2435 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2436 XtSetArg(args[0], XtNlabel, buf);
2438 snprintf(buf, MSG_SIZ, "%s ", color);
2439 XtSetArg(args[0], XtNlabel, buf);
2444 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2445 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2447 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2448 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2451 XtSetValues(w, args, 3);
2454 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2457 SetClockIcon (int color)
2460 Pixmap pm = *clockIcons[color];
2461 if (iconPixmap != pm) {
2463 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2464 XtSetValues(shellWidget, args, 1);
2469 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2471 InputSource *is = (InputSource *) closure;
2476 if (is->lineByLine) {
2477 count = read(is->fd, is->unused,
2478 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2480 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2483 is->unused += count;
2485 while (p < is->unused) {
2486 q = memchr(p, '\n', is->unused - p);
2487 if (q == NULL) break;
2489 (is->func)(is, is->closure, p, q - p, 0);
2493 while (p < is->unused) {
2498 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2503 (is->func)(is, is->closure, is->buf, count, error);
2508 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2511 ChildProc *cp = (ChildProc *) pr;
2513 is = (InputSource *) calloc(1, sizeof(InputSource));
2514 is->lineByLine = lineByLine;
2518 is->fd = fileno(stdin);
2520 is->kind = cp->kind;
2521 is->fd = cp->fdFrom;
2524 is->unused = is->buf;
2527 is->xid = XtAppAddInput(appContext, is->fd,
2528 (XtPointer) (XtInputReadMask),
2529 (XtInputCallbackProc) DoInputCallback,
2531 is->closure = closure;
2532 return (InputSourceRef) is;
2536 RemoveInputSource (InputSourceRef isr)
2538 InputSource *is = (InputSource *) isr;
2540 if (is->xid == 0) return;
2541 XtRemoveInput(is->xid);
2547 static Boolean frameWaiting;
2550 FrameAlarm (int sig)
2552 frameWaiting = False;
2553 /* In case System-V style signals. Needed?? */
2554 signal(SIGALRM, FrameAlarm);
2558 FrameDelay (int time)
2560 struct itimerval delay;
2562 XSync(xDisplay, False);
2565 frameWaiting = True;
2566 signal(SIGALRM, FrameAlarm);
2567 delay.it_interval.tv_sec =
2568 delay.it_value.tv_sec = time / 1000;
2569 delay.it_interval.tv_usec =
2570 delay.it_value.tv_usec = (time % 1000) * 1000;
2571 setitimer(ITIMER_REAL, &delay, NULL);
2572 while (frameWaiting) pause();
2573 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2574 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2575 setitimer(ITIMER_REAL, &delay, NULL);
2582 FrameDelay (int time)
2584 XSync(xDisplay, False);
2586 usleep(time * 1000);
2592 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2594 char buf[MSG_SIZ], *logoName = buf;
2595 if(appData.logo[n][0]) {
2596 logoName = appData.logo[n];
2597 } else if(appData.autoLogo) {
2598 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2599 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2600 } else if(appData.directory[n] && appData.directory[n][0]) {
2601 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2605 { ASSIGN(cps->programLogo, logoName); }
2609 UpdateLogos (int displ)
2611 if(optList[W_WHITE-1].handle == NULL) return;
2612 LoadLogo(&first, 0, 0);
2613 LoadLogo(&second, 1, appData.icsActive);
2614 if(displ) DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);