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>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
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 "xedittags.h"
209 #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 static void CreateGCs P((int redo));
234 static void CreateAnyPieces P((void));
235 void CreateXIMPieces P((void));
236 void CreateXPMPieces P((void));
237 void CreateXPMBoard P((char *s, int n));
238 void CreatePieces P((void));
239 Widget CreateMenuBar P((Menu *mb, int boardWidth));
241 char *InsertPxlSize P((char *pattern, int targetPxlSize));
242 XFontSet CreateFontSet P((char *base_fnt_lst));
244 char *FindFont P((char *pattern, int targetPxlSize));
246 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
247 u_int wreq, u_int hreq));
248 void CreateGrid P((void));
249 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
250 void DelayedDrag P((void));
251 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
252 void HandlePV P((Widget w, XEvent * event,
253 String * params, Cardinal * nParams));
254 void DrawPositionProc P((Widget w, XEvent *event,
255 String *prms, Cardinal *nprms));
256 void CommentClick P((Widget w, XEvent * event,
257 String * params, Cardinal * nParams));
258 void ICSInputBoxPopUp P((void));
259 void FileNamePopUp P((char *label, char *def, char *filter,
260 FileProc proc, char *openMode));
261 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
262 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
263 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
264 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
265 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
266 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
267 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
268 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
269 Boolean TempBackwardActive = False;
270 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
271 void DisplayMove P((int moveNumber));
272 void ICSInitScript P((void));
273 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
274 void update_ics_width P(());
275 int get_term_width P(());
276 int CopyMemoProc P(());
277 void SetupDropMenu P((void));
280 * XBoard depends on Xt R4 or higher
282 int xtVersion = XtSpecificationRelease;
287 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
288 highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
289 Pixel lowTimeWarningColor;
290 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
291 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
293 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
294 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
295 Option *optList; // contains all widgets of main window
296 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
297 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
299 XFontSet fontSet, clockFontSet;
302 XFontStruct *clockFontStruct;
304 Font coordFontID, countFontID;
305 XFontStruct *coordFontStruct, *countFontStruct;
306 XtAppContext appContext;
311 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
313 Position commentX = -1, commentY = -1;
314 Dimension commentW, commentH;
315 typedef unsigned int BoardSize;
317 Boolean chessProgram;
319 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
320 int smallLayout = 0, tinyLayout = 0,
321 marginW, marginH, // [HGM] for run-time resizing
322 fromX = -1, fromY = -1, toX, toY, commentUp = False,
323 errorExitStatus = -1, defaultLineGap;
324 Dimension textHeight;
325 Pixel timerForegroundPixel, timerBackgroundPixel;
326 Pixel buttonForegroundPixel, buttonBackgroundPixel;
327 char *chessDir, *programName, *programVersion;
328 Boolean alwaysOnTop = False;
329 char *icsTextMenuString;
331 char *firstChessProgramNames;
332 char *secondChessProgramNames;
334 WindowPlacement wpMain;
335 WindowPlacement wpConsole;
336 WindowPlacement wpComment;
337 WindowPlacement wpMoveHistory;
338 WindowPlacement wpEvalGraph;
339 WindowPlacement wpEngineOutput;
340 WindowPlacement wpGameList;
341 WindowPlacement wpTags;
346 Pixmap pieceBitmap[2][(int)BlackPawn];
347 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
348 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
349 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
350 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
351 Pixmap xpmBoardBitmap[2];
352 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
353 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
354 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
355 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
356 XImage *ximLightSquare, *ximDarkSquare;
359 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
360 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
362 #define White(piece) ((int)(piece) < (int)BlackPawn)
364 /* Bitmaps for use as masks when drawing XPM pieces.
365 Need one for each black and white piece. */
366 static Pixmap xpmMask[BlackKing + 1];
368 /* This magic number is the number of intermediate frames used
369 in each half of the animation. For short moves it's reduced
370 by 1. The total number of frames will be factor * 2 + 1. */
373 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
380 DropMenuEnables dmEnables[] = {
397 XtResource clientResources[] = {
398 { "flashCount", "flashCount", XtRInt, sizeof(int),
399 XtOffset(AppDataPtr, flashCount), XtRImmediate,
400 (XtPointer) FLASH_COUNT },
403 XrmOptionDescRec shellOptions[] = {
404 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
405 { "-flash", "flashCount", XrmoptionNoArg, "3" },
406 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
409 XtActionsRec boardActions[] = {
410 { "DrawPosition", DrawPositionProc },
411 { "HandlePV", HandlePV },
412 { "SelectPV", SelectPV },
413 { "StopPV", StopPV },
414 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
415 { "QuitProc", QuitWrapper },
416 { "ManProc", ManInner },
417 { "TempBackwardProc", TempBackwardProc },
418 { "TempForwardProc", TempForwardProc },
419 { "CommentClick", (XtActionProc) CommentClick },
420 { "GenericPopDown", (XtActionProc) GenericPopDown },
421 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
422 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
423 { "SelectMove", (XtActionProc) SelectMove },
424 { "LoadSelectedProc", LoadSelectedProc },
425 { "SetFilterProc", SetFilterProc },
426 { "TypeInProc", TypeInProc },
427 { "EnterKeyProc", EnterKeyProc },
428 { "UpKeyProc", UpKeyProc },
429 { "DownKeyProc", DownKeyProc },
430 { "WheelProc", WheelProc },
431 { "TabProc", TabProc },
434 char globalTranslations[] =
435 ":<Key>F9: MenuItem(Actions.Resign) \n \
436 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
437 :Meta<Key>V: MenuItem(File.NewVariant) \n \
438 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
439 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
440 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
441 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
442 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
443 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
444 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
445 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
446 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
447 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
448 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
449 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
450 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
451 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
452 :Ctrl<Key>q: MenuItem(File.Quit) \n \
453 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
454 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
455 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
456 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
457 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
458 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
459 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
460 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
461 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
462 :Meta<Key>G: MenuItem(View.GameList) \n \
463 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
464 :<Key>Pause: MenuItem(Mode.Pause) \n \
465 :<Key>F3: MenuItem(Action.Accept) \n \
466 :<Key>F4: MenuItem(Action.Decline) \n \
467 :<Key>F12: MenuItem(Action.Rematch) \n \
468 :<Key>F5: MenuItem(Action.CallFlag) \n \
469 :<Key>F6: MenuItem(Action.Draw) \n \
470 :<Key>F7: MenuItem(Action.Adjourn) \n \
471 :<Key>F8: MenuItem(Action.Abort) \n \
472 :<Key>F10: MenuItem(Action.StopObserving) \n \
473 :<Key>F11: MenuItem(Action.StopExamining) \n \
474 :Ctrl<Key>d: MenuItem(DebugProc) \n \
475 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
476 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
477 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
478 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
479 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
480 :<Key>Left: MenuItem(Edit.Backward) \n \
481 :<Key>Right: MenuItem(Edit.Forward) \n \
482 :<Key>Home: MenuItem(Edit.Revert) \n \
483 :<Key>End: MenuItem(Edit.TruncateGame) \n \
484 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
485 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
486 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
487 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
488 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
489 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
490 #ifndef OPTIONSDIALOG
492 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
493 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
494 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
495 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
496 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
499 :<Key>F1: MenuItem(Help.ManXBoard) \n \
500 :<Key>F2: MenuItem(View.FlipView) \n \
501 :<KeyDown>Return: TempBackwardProc() \n \
502 :<KeyUp>Return: TempForwardProc() \n";
504 char ICSInputTranslations[] =
505 "<Key>Up: UpKeyProc() \n "
506 "<Key>Down: DownKeyProc() \n "
507 "<Key>Return: EnterKeyProc() \n";
509 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
510 // as the widget is destroyed before the up-click can call extend-end
511 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
513 String xboardResources[] = {
514 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
519 /* Max possible square size */
520 #define MAXSQSIZE 256
522 static int xpm_avail[MAXSQSIZE];
524 #ifdef HAVE_DIR_STRUCT
526 /* Extract piece size from filename */
528 xpm_getsize (char *name, int len, char *ext)
536 if ((p=strchr(name, '.')) == NULL ||
537 StrCaseCmp(p+1, ext) != 0)
543 while (*p && isdigit(*p))
550 /* Setup xpm_avail */
552 xpm_getavail (char *dirname, char *ext)
558 for (i=0; i<MAXSQSIZE; ++i)
561 if (appData.debugMode)
562 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
564 dir = opendir(dirname);
567 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
568 programName, dirname);
572 while ((ent=readdir(dir)) != NULL) {
573 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
574 if (i > 0 && i < MAXSQSIZE)
584 xpm_print_avail (FILE *fp, char *ext)
588 fprintf(fp, _("Available `%s' sizes:\n"), ext);
589 for (i=1; i<MAXSQSIZE; ++i) {
595 /* Return XPM piecesize closest to size */
597 xpm_closest_to (char *dirname, int size, char *ext)
600 int sm_diff = MAXSQSIZE;
604 xpm_getavail(dirname, ext);
606 if (appData.debugMode)
607 xpm_print_avail(stderr, ext);
609 for (i=1; i<MAXSQSIZE; ++i) {
612 diff = (diff<0) ? -diff : diff;
613 if (diff < sm_diff) {
621 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
627 #else /* !HAVE_DIR_STRUCT */
628 /* If we are on a system without a DIR struct, we can't
629 read the directory, so we can't collect a list of
630 filenames, etc., so we can't do any size-fitting. */
632 xpm_closest_to (char *dirname, int size, char *ext)
635 Warning: No DIR structure found on this system --\n\
636 Unable to autosize for XPM/XIM pieces.\n\
637 Please report this error to %s.\n\
638 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
641 #endif /* HAVE_DIR_STRUCT */
644 /* Arrange to catch delete-window events */
645 Atom wm_delete_window;
647 CatchDeleteWindow (Widget w, String procname)
650 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
651 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
652 XtAugmentTranslations(w, XtParseTranslationTable(buf));
659 XtSetArg(args[0], XtNiconic, False);
660 XtSetValues(shellWidget, args, 1);
662 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
665 //---------------------------------------------------------------------------------------------------------
666 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
669 #define CW_USEDEFAULT (1<<31)
670 #define ICS_TEXT_MENU_SIZE 90
671 #define DEBUG_FILE "xboard.debug"
672 #define SetCurrentDirectory chdir
673 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
677 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
680 // front-end part of option handling
682 // [HGM] This platform-dependent table provides the location for storing the color info
683 extern char *crWhite, * crBlack;
687 &appData.whitePieceColor,
688 &appData.blackPieceColor,
689 &appData.lightSquareColor,
690 &appData.darkSquareColor,
691 &appData.highlightSquareColor,
692 &appData.premoveHighlightColor,
693 &appData.lowTimeWarningColor,
704 // [HGM] font: keep a font for each square size, even non-stndard ones
707 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
708 char *fontTable[NUM_FONTS][MAX_SIZE];
711 ParseFont (char *name, int number)
712 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
714 if(sscanf(name, "size%d:", &size)) {
715 // [HGM] font: font is meant for specific boardSize (likely from settings file);
716 // defer processing it until we know if it matches our board size
717 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
718 fontTable[number][size] = strdup(strchr(name, ':')+1);
719 fontValid[number][size] = True;
724 case 0: // CLOCK_FONT
725 appData.clockFont = strdup(name);
727 case 1: // MESSAGE_FONT
728 appData.font = strdup(name);
730 case 2: // COORD_FONT
731 appData.coordFont = strdup(name);
736 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
741 { // only 2 fonts currently
742 appData.clockFont = CLOCK_FONT_NAME;
743 appData.coordFont = COORD_FONT_NAME;
744 appData.font = DEFAULT_FONT_NAME;
749 { // no-op, until we identify the code for this already in XBoard and move it here
753 ParseColor (int n, char *name)
754 { // in XBoard, just copy the color-name string
755 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
759 ParseTextAttribs (ColorClass cc, char *s)
761 (&appData.colorShout)[cc] = strdup(s);
765 ParseBoardSize (void *addr, char *name)
767 appData.boardSize = strdup(name);
772 { // In XBoard the sound-playing program takes care of obtaining the actual sound
776 SetCommPortDefaults ()
777 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
780 // [HGM] args: these three cases taken out to stay in front-end
782 SaveFontArg (FILE *f, ArgDescriptor *ad)
785 int i, n = (int)(intptr_t)ad->argLoc;
787 case 0: // CLOCK_FONT
788 name = appData.clockFont;
790 case 1: // MESSAGE_FONT
793 case 2: // COORD_FONT
794 name = appData.coordFont;
799 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
800 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
801 fontTable[n][squareSize] = strdup(name);
802 fontValid[n][squareSize] = True;
805 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
806 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
811 { // nothing to do, as the sounds are at all times represented by their text-string names already
815 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
816 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
817 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
821 SaveColor (FILE *f, ArgDescriptor *ad)
822 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
823 if(colorVariable[(int)(intptr_t)ad->argLoc])
824 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
828 SaveBoardSize (FILE *f, char *name, void *addr)
829 { // wrapper to shield back-end from BoardSize & sizeInfo
830 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
834 ParseCommPortSettings (char *s)
835 { // no such option in XBoard (yet)
841 GetActualPlacement (Widget wg, WindowPlacement *wp)
846 XWindowAttributes winAt;
853 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
854 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
855 wp->x = rx - winAt.x;
856 wp->y = ry - winAt.y;
857 wp->height = winAt.height;
858 wp->width = winAt.width;
859 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
864 { // wrapper to shield use of window handles from back-end (make addressible by number?)
865 // In XBoard this will have to wait until awareness of window parameters is implemented
866 GetActualPlacement(shellWidget, &wpMain);
867 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
868 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
869 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
870 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
871 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
872 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
876 PrintCommPortSettings (FILE *f, char *name)
877 { // This option does not exist in XBoard
881 EnsureOnScreen (int *x, int *y, int minX, int minY)
888 { // [HGM] args: allows testing if main window is realized from back-end
889 return xBoardWindow != 0;
893 PopUpStartupDialog ()
894 { // start menu not implemented in XBoard
898 ConvertToLine (int argc, char **argv)
900 static char line[128*1024], buf[1024];
904 for(i=1; i<argc; i++)
906 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
907 && argv[i][0] != '{' )
908 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
910 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
911 strncat(line, buf, 128*1024 - strlen(line) - 1 );
914 line[strlen(line)-1] = NULLCHAR;
918 //--------------------------------------------------------------------------------------------
920 #define BoardSize int
922 InitDrawingSizes (BoardSize boardSize, int flags)
923 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
924 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
926 XtGeometryResult gres;
928 static Dimension oldWidth, oldHeight;
929 static VariantClass oldVariant;
930 static int oldDual = -1, oldMono = -1;
932 if(!formWidget) return;
934 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
935 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
936 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
938 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
940 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
942 hOffset = boardWidth + 10;
943 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
944 secondSegments[i] = gridSegments[i];
945 secondSegments[i].x1 += hOffset;
946 secondSegments[i].x2 += hOffset;
950 * Inhibit shell resizing.
952 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
953 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
954 shellArgs[4].value = shellArgs[2].value = w;
955 shellArgs[5].value = shellArgs[3].value = h;
956 XtSetValues(shellWidget, &shellArgs[0], 6);
958 XSync(xDisplay, False);
962 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
965 if(gameInfo.variant != oldVariant) { // and only if variant changed
970 for(p=0; p<=(int)WhiteKing; p++)
971 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
972 if(gameInfo.variant == VariantShogi) {
973 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
974 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
975 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
976 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
977 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
980 if(gameInfo.variant == VariantGothic) {
981 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
984 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
985 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
986 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
989 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
990 for(p=0; p<=(int)WhiteKing; p++)
991 ximMaskPm[p] = ximMaskPm2[p]; // defaults
992 if(gameInfo.variant == VariantShogi) {
993 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
994 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
995 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
996 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
997 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1000 if(gameInfo.variant == VariantGothic) {
1001 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1004 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1005 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1006 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1011 for(i=0; i<2; i++) {
1013 for(p=0; p<=(int)WhiteKing; p++)
1014 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1015 if(gameInfo.variant == VariantShogi) {
1016 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1017 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1018 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1019 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1020 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1023 if(gameInfo.variant == VariantGothic) {
1024 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1027 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1028 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1029 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1033 oldMono = -10; // kludge to force recreation of animation masks
1034 oldVariant = gameInfo.variant;
1037 if(appData.monoMode != oldMono)
1040 oldMono = appData.monoMode;
1044 MakeOneColor (char *name, Pixel *color)
1046 XrmValue vFrom, vTo;
1047 if (!appData.monoMode) {
1048 vFrom.addr = (caddr_t) name;
1049 vFrom.size = strlen(name);
1050 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1051 if (vTo.addr == NULL) {
1052 appData.monoMode = True;
1055 *color = *(Pixel *) vTo.addr;
1063 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1064 int forceMono = False;
1066 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1067 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1068 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1069 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1070 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1071 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1072 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1073 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1080 { // [HGM] taken out of main
1082 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1083 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1084 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1086 if (appData.bitmapDirectory[0] != NULLCHAR) {
1090 CreateXPMBoard(appData.liteBackTextureFile, 1);
1091 CreateXPMBoard(appData.darkBackTextureFile, 0);
1095 /* Create regular pieces */
1096 if (!useImages) CreatePieces();
1101 InitDrawingParams ()
1103 MakeColors(); CreateGCs(True);
1108 main (int argc, char **argv)
1110 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1111 XSetWindowAttributes window_attributes;
1113 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1114 XrmValue vFrom, vTo;
1115 XtGeometryResult gres;
1118 int forceMono = False;
1120 srandom(time(0)); // [HGM] book: make random truly random
1122 setbuf(stdout, NULL);
1123 setbuf(stderr, NULL);
1126 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1127 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1131 programName = strrchr(argv[0], '/');
1132 if (programName == NULL)
1133 programName = argv[0];
1138 XtSetLanguageProc(NULL, NULL, NULL);
1139 bindtextdomain(PACKAGE, LOCALEDIR);
1140 textdomain(PACKAGE);
1144 XtAppInitialize(&appContext, "XBoard", shellOptions,
1145 XtNumber(shellOptions),
1146 &argc, argv, xboardResources, NULL, 0);
1147 appData.boardSize = "";
1148 InitAppData(ConvertToLine(argc, argv));
1150 if (p == NULL) p = "/tmp";
1151 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1152 gameCopyFilename = (char*) malloc(i);
1153 gamePasteFilename = (char*) malloc(i);
1154 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1155 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1157 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1158 clientResources, XtNumber(clientResources),
1161 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1162 static char buf[MSG_SIZ];
1163 EscapeExpand(buf, appData.firstInitString);
1164 appData.firstInitString = strdup(buf);
1165 EscapeExpand(buf, appData.secondInitString);
1166 appData.secondInitString = strdup(buf);
1167 EscapeExpand(buf, appData.firstComputerString);
1168 appData.firstComputerString = strdup(buf);
1169 EscapeExpand(buf, appData.secondComputerString);
1170 appData.secondComputerString = strdup(buf);
1173 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1176 if (chdir(chessDir) != 0) {
1177 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1183 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1184 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1185 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1186 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1189 setbuf(debugFP, NULL);
1193 if (appData.debugMode) {
1194 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1198 /* [HGM,HR] make sure board size is acceptable */
1199 if(appData.NrFiles > BOARD_FILES ||
1200 appData.NrRanks > BOARD_RANKS )
1201 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1204 /* This feature does not work; animation needs a rewrite */
1205 appData.highlightDragging = FALSE;
1209 xDisplay = XtDisplay(shellWidget);
1210 xScreen = DefaultScreen(xDisplay);
1211 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1213 gameInfo.variant = StringToVariant(appData.variant);
1214 InitPosition(FALSE);
1216 if (isdigit(appData.boardSize[0])) {
1217 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1218 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1219 &fontPxlSize, &smallLayout, &tinyLayout);
1221 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1222 programName, appData.boardSize);
1226 /* Find some defaults; use the nearest known size */
1227 SizeDefaults *szd, *nearest;
1228 int distance = 99999;
1229 nearest = szd = sizeDefaults;
1230 while (szd->name != NULL) {
1231 if (abs(szd->squareSize - squareSize) < distance) {
1233 distance = abs(szd->squareSize - squareSize);
1234 if (distance == 0) break;
1238 if (i < 2) lineGap = nearest->lineGap;
1239 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1240 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1241 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1242 if (i < 6) smallLayout = nearest->smallLayout;
1243 if (i < 7) tinyLayout = nearest->tinyLayout;
1246 SizeDefaults *szd = sizeDefaults;
1247 if (*appData.boardSize == NULLCHAR) {
1248 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1249 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1252 if (szd->name == NULL) szd--;
1253 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1255 while (szd->name != NULL &&
1256 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1257 if (szd->name == NULL) {
1258 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1259 programName, appData.boardSize);
1263 squareSize = szd->squareSize;
1264 lineGap = szd->lineGap;
1265 clockFontPxlSize = szd->clockFontPxlSize;
1266 coordFontPxlSize = szd->coordFontPxlSize;
1267 fontPxlSize = szd->fontPxlSize;
1268 smallLayout = szd->smallLayout;
1269 tinyLayout = szd->tinyLayout;
1270 // [HGM] font: use defaults from settings file if available and not overruled
1272 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1273 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1274 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1275 appData.font = fontTable[MESSAGE_FONT][squareSize];
1276 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1277 appData.coordFont = fontTable[COORD_FONT][squareSize];
1279 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1280 if (strlen(appData.pixmapDirectory) > 0) {
1281 p = ExpandPathName(appData.pixmapDirectory);
1283 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1284 appData.pixmapDirectory);
1287 if (appData.debugMode) {
1288 fprintf(stderr, _("\
1289 XBoard square size (hint): %d\n\
1290 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1292 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1293 if (appData.debugMode) {
1294 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1297 defaultLineGap = lineGap;
1298 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1300 /* [HR] height treated separately (hacked) */
1301 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1302 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1305 * Determine what fonts to use.
1308 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1309 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1310 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1311 fontSet = CreateFontSet(appData.font);
1312 clockFontSet = CreateFontSet(appData.clockFont);
1314 /* For the coordFont, use the 0th font of the fontset. */
1315 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1316 XFontStruct **font_struct_list;
1317 XFontSetExtents *fontSize;
1318 char **font_name_list;
1319 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1320 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1321 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1322 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1323 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1326 appData.font = FindFont(appData.font, fontPxlSize);
1327 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1328 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1329 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1330 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1331 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1332 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1333 // textHeight in !NLS mode!
1335 countFontID = coordFontID; // [HGM] holdings
1336 countFontStruct = coordFontStruct;
1338 xdb = XtDatabase(xDisplay);
1340 XrmPutLineResource(&xdb, "*international: True");
1341 vTo.size = sizeof(XFontSet);
1342 vTo.addr = (XtPointer) &fontSet;
1343 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1345 XrmPutStringResource(&xdb, "*font", appData.font);
1349 * Detect if there are not enough colors available and adapt.
1351 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1352 appData.monoMode = True;
1355 forceMono = MakeColors();
1358 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1360 appData.monoMode = True;
1363 if (appData.lowTimeWarning && !appData.monoMode) {
1364 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1365 vFrom.size = strlen(appData.lowTimeWarningColor);
1366 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1367 if (vTo.addr == NULL)
1368 appData.monoMode = True;
1370 lowTimeWarningColor = *(Pixel *) vTo.addr;
1373 if (appData.monoMode && appData.debugMode) {
1374 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1375 (unsigned long) XWhitePixel(xDisplay, xScreen),
1376 (unsigned long) XBlackPixel(xDisplay, xScreen));
1379 ParseIcsTextColors();
1381 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1387 layoutName = "tinyLayout";
1388 } else if (smallLayout) {
1389 layoutName = "smallLayout";
1391 layoutName = "normalLayout";
1394 optList = BoardPopUp(squareSize, lineGap, (void*)
1400 boardWidget = optList[22].handle;
1401 menuBarWidget = optList[ 0].handle;
1402 dropMenu = optList[25].handle;
1403 titleWidget = optList[optList[10].type != -1 ? 10 : 13].handle;
1404 formWidget = XtParent(boardWidget);
1405 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1406 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1407 XtGetValues(optList[11].handle, args, 2);
1408 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1409 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1410 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1411 XtGetValues(optList[18].handle, args, 2);
1413 AppendEnginesToMenu(appData.recentEngineList);
1415 xBoardWindow = XtWindow(boardWidget);
1417 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1418 // not need to go into InitDrawingSizes().
1421 * Create X checkmark bitmap and initialize option menu checks.
1423 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1424 checkmark_bits, checkmark_width, checkmark_height);
1430 ReadBitmap(&wIconPixmap, "icon_white.bm",
1431 icon_white_bits, icon_white_width, icon_white_height);
1432 ReadBitmap(&bIconPixmap, "icon_black.bm",
1433 icon_black_bits, icon_black_width, icon_black_height);
1434 iconPixmap = wIconPixmap;
1436 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1437 XtSetValues(shellWidget, args, i);
1440 * Create a cursor for the board widget.
1442 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1443 XChangeWindowAttributes(xDisplay, xBoardWindow,
1444 CWCursor, &window_attributes);
1447 * Inhibit shell resizing.
1449 shellArgs[0].value = (XtArgVal) &w;
1450 shellArgs[1].value = (XtArgVal) &h;
1451 XtGetValues(shellWidget, shellArgs, 2);
1452 shellArgs[4].value = shellArgs[2].value = w;
1453 shellArgs[5].value = shellArgs[3].value = h;
1454 XtSetValues(shellWidget, &shellArgs[2], 4);
1455 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1456 marginH = h - boardHeight;
1458 CatchDeleteWindow(shellWidget, "QuitProc");
1464 if (appData.animate || appData.animateDragging)
1467 XtAugmentTranslations(formWidget,
1468 XtParseTranslationTable(globalTranslations));
1470 XtAddEventHandler(formWidget, KeyPressMask, False,
1471 (XtEventHandler) MoveTypeInProc, NULL);
1472 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1473 (XtEventHandler) EventProc, NULL);
1475 /* [AS] Restore layout */
1476 if( wpMoveHistory.visible ) {
1480 if( wpEvalGraph.visible )
1485 if( wpEngineOutput.visible ) {
1486 EngineOutputPopUp();
1491 if (errorExitStatus == -1) {
1492 if (appData.icsActive) {
1493 /* We now wait until we see "login:" from the ICS before
1494 sending the logon script (problems with timestamp otherwise) */
1495 /*ICSInitScript();*/
1496 if (appData.icsInputBox) ICSInputBoxPopUp();
1500 signal(SIGWINCH, TermSizeSigHandler);
1502 signal(SIGINT, IntSigHandler);
1503 signal(SIGTERM, IntSigHandler);
1504 if (*appData.cmailGameName != NULLCHAR) {
1505 signal(SIGUSR1, CmailSigHandler);
1509 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1511 // XtSetKeyboardFocus(shellWidget, formWidget);
1512 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1514 XtAppMainLoop(appContext);
1515 if (appData.debugMode) fclose(debugFP); // [DM] debug
1520 TermSizeSigHandler (int sig)
1526 IntSigHandler (int sig)
1532 CmailSigHandler (int sig)
1537 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1539 /* Activate call-back function CmailSigHandlerCallBack() */
1540 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1542 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1546 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1549 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1551 /**** end signal code ****/
1554 #define Abs(n) ((n)<0 ? -(n) : (n))
1558 InsertPxlSize (char *pattern, int targetPxlSize)
1560 char *base_fnt_lst, strInt[12], *p, *q;
1561 int alternatives, i, len, strIntLen;
1564 * Replace the "*" (if present) in the pixel-size slot of each
1565 * alternative with the targetPxlSize.
1569 while ((p = strchr(p, ',')) != NULL) {
1573 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1574 strIntLen = strlen(strInt);
1575 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1579 while (alternatives--) {
1580 char *comma = strchr(p, ',');
1581 for (i=0; i<14; i++) {
1582 char *hyphen = strchr(p, '-');
1584 if (comma && hyphen > comma) break;
1585 len = hyphen + 1 - p;
1586 if (i == 7 && *p == '*' && len == 2) {
1588 memcpy(q, strInt, strIntLen);
1598 len = comma + 1 - p;
1605 return base_fnt_lst;
1609 CreateFontSet (char *base_fnt_lst)
1612 char **missing_list;
1616 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1617 &missing_list, &missing_count, &def_string);
1618 if (appData.debugMode) {
1620 XFontStruct **font_struct_list;
1621 char **font_name_list;
1622 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1624 fprintf(debugFP, " got list %s, locale %s\n",
1625 XBaseFontNameListOfFontSet(fntSet),
1626 XLocaleOfFontSet(fntSet));
1627 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1628 for (i = 0; i < count; i++) {
1629 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1632 for (i = 0; i < missing_count; i++) {
1633 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1636 if (fntSet == NULL) {
1637 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1642 #else // not ENABLE_NLS
1644 * Find a font that matches "pattern" that is as close as
1645 * possible to the targetPxlSize. Prefer fonts that are k
1646 * pixels smaller to fonts that are k pixels larger. The
1647 * pattern must be in the X Consortium standard format,
1648 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1649 * The return value should be freed with XtFree when no
1653 FindFont (char *pattern, int targetPxlSize)
1655 char **fonts, *p, *best, *scalable, *scalableTail;
1656 int i, j, nfonts, minerr, err, pxlSize;
1658 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1660 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1661 programName, pattern);
1668 for (i=0; i<nfonts; i++) {
1671 if (*p != '-') continue;
1673 if (*p == NULLCHAR) break;
1674 if (*p++ == '-') j++;
1676 if (j < 7) continue;
1679 scalable = fonts[i];
1682 err = pxlSize - targetPxlSize;
1683 if (Abs(err) < Abs(minerr) ||
1684 (minerr > 0 && err < 0 && -err == minerr)) {
1690 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1691 /* If the error is too big and there is a scalable font,
1692 use the scalable font. */
1693 int headlen = scalableTail - scalable;
1694 p = (char *) XtMalloc(strlen(scalable) + 10);
1695 while (isdigit(*scalableTail)) scalableTail++;
1696 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1698 p = (char *) XtMalloc(strlen(best) + 2);
1699 safeStrCpy(p, best, strlen(best)+1 );
1701 if (appData.debugMode) {
1702 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1703 pattern, targetPxlSize, p);
1705 XFreeFontNames(fonts);
1712 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1713 // must be called before all non-first callse to CreateGCs()
1714 XtReleaseGC(shellWidget, highlineGC);
1715 XtReleaseGC(shellWidget, lightSquareGC);
1716 XtReleaseGC(shellWidget, darkSquareGC);
1717 XtReleaseGC(shellWidget, lineGC);
1718 if (appData.monoMode) {
1719 if (DefaultDepth(xDisplay, xScreen) == 1) {
1720 XtReleaseGC(shellWidget, wbPieceGC);
1722 XtReleaseGC(shellWidget, bwPieceGC);
1725 XtReleaseGC(shellWidget, prelineGC);
1726 XtReleaseGC(shellWidget, wdPieceGC);
1727 XtReleaseGC(shellWidget, wlPieceGC);
1728 XtReleaseGC(shellWidget, bdPieceGC);
1729 XtReleaseGC(shellWidget, blPieceGC);
1734 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1736 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1737 | GCBackground | GCFunction | GCPlaneMask;
1738 gc_values->foreground = foreground;
1739 gc_values->background = background;
1740 return XtGetGC(shellWidget, value_mask, gc_values);
1744 CreateGCs (int redo)
1746 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1747 | GCBackground | GCFunction | GCPlaneMask;
1748 XGCValues gc_values;
1750 Pixel white = XWhitePixel(xDisplay, xScreen);
1751 Pixel black = XBlackPixel(xDisplay, xScreen);
1753 gc_values.plane_mask = AllPlanes;
1754 gc_values.line_width = lineGap;
1755 gc_values.line_style = LineSolid;
1756 gc_values.function = GXcopy;
1759 DeleteGCs(); // called a second time; clean up old GCs first
1760 } else { // [HGM] grid and font GCs created on first call only
1761 coordGC = CreateOneGC(&gc_values, black, white);
1762 XSetFont(xDisplay, coordGC, coordFontID);
1764 // [HGM] make font for holdings counts (white on black)
1765 countGC = CreateOneGC(&gc_values, white, black);
1766 XSetFont(xDisplay, countGC, countFontID);
1768 lineGC = CreateOneGC(&gc_values, black, black);
1770 if (appData.monoMode) {
1772 highlineGC = CreateOneGC(&gc_values, white, white);
1773 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1774 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1776 if (DefaultDepth(xDisplay, xScreen) == 1) {
1777 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1778 gc_values.function = GXcopyInverted;
1779 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1780 gc_values.function = GXcopy;
1781 if (XBlackPixel(xDisplay, xScreen) == 1) {
1782 bwPieceGC = darkSquareGC;
1783 wbPieceGC = copyInvertedGC;
1785 bwPieceGC = copyInvertedGC;
1786 wbPieceGC = lightSquareGC;
1791 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1792 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1793 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1794 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1795 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1796 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1797 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1798 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1803 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1811 fp = fopen(filename, "rb");
1813 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1820 for (y=0; y<h; ++y) {
1821 for (x=0; x<h; ++x) {
1826 XPutPixel(xim, x, y, blackPieceColor);
1828 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1831 XPutPixel(xim, x, y, darkSquareColor);
1833 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1836 XPutPixel(xim, x, y, whitePieceColor);
1838 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1841 XPutPixel(xim, x, y, lightSquareColor);
1843 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1851 /* create Pixmap of piece */
1852 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1854 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1857 /* create Pixmap of clipmask
1858 Note: We assume the white/black pieces have the same
1859 outline, so we make only 6 masks. This is okay
1860 since the XPM clipmask routines do the same. */
1862 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1864 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1867 /* now create the 1-bit version */
1868 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1871 values.foreground = 1;
1872 values.background = 0;
1874 /* Don't use XtGetGC, not read only */
1875 maskGC = XCreateGC(xDisplay, *mask,
1876 GCForeground | GCBackground, &values);
1877 XCopyPlane(xDisplay, temp, *mask, maskGC,
1878 0, 0, squareSize, squareSize, 0, 0, 1);
1879 XFreePixmap(xDisplay, temp);
1884 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1892 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1897 /* The XSynchronize calls were copied from CreatePieces.
1898 Not sure if needed, but can't hurt */
1899 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1902 /* temp needed by loadXIM() */
1903 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1904 0, 0, ss, ss, AllPlanes, XYPixmap);
1906 if (strlen(appData.pixmapDirectory) == 0) {
1910 if (appData.monoMode) {
1911 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1915 fprintf(stderr, _("\nLoading XIMs...\n"));
1917 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1918 fprintf(stderr, "%d", piece+1);
1919 for (kind=0; kind<4; kind++) {
1920 fprintf(stderr, ".");
1921 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
1922 ExpandPathName(appData.pixmapDirectory),
1923 piece <= (int) WhiteKing ? "" : "w",
1924 pieceBitmapNames[piece],
1926 ximPieceBitmap[kind][piece] =
1927 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1928 0, 0, ss, ss, AllPlanes, XYPixmap);
1929 if (appData.debugMode)
1930 fprintf(stderr, _("(File:%s:) "), buf);
1931 loadXIM(ximPieceBitmap[kind][piece],
1933 &(xpmPieceBitmap2[kind][piece]),
1934 &(ximMaskPm2[piece]));
1935 if(piece <= (int)WhiteKing)
1936 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
1938 fprintf(stderr," ");
1940 /* Load light and dark squares */
1941 /* If the LSQ and DSQ pieces don't exist, we will
1942 draw them with solid squares. */
1943 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
1944 if (access(buf, 0) != 0) {
1948 fprintf(stderr, _("light square "));
1950 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1951 0, 0, ss, ss, AllPlanes, XYPixmap);
1952 if (appData.debugMode)
1953 fprintf(stderr, _("(File:%s:) "), buf);
1955 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
1956 fprintf(stderr, _("dark square "));
1957 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
1958 ExpandPathName(appData.pixmapDirectory), ss);
1959 if (appData.debugMode)
1960 fprintf(stderr, _("(File:%s:) "), buf);
1962 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1963 0, 0, ss, ss, AllPlanes, XYPixmap);
1964 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
1965 xpmJailSquare = xpmLightSquare;
1967 fprintf(stderr, _("Done.\n"));
1969 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
1972 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
1976 CreateXPMBoard (char *s, int kind)
1980 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
1981 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
1982 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
1988 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
1989 // thisroutine has to be called t free the old piece pixmaps
1991 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
1992 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
1994 XFreePixmap(xDisplay, xpmLightSquare);
1995 XFreePixmap(xDisplay, xpmDarkSquare);
2004 u_int ss = squareSize;
2006 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2007 XpmColorSymbol symbols[4];
2008 static int redo = False;
2010 if(redo) FreeXPMPieces(); else redo = 1;
2012 /* The XSynchronize calls were copied from CreatePieces.
2013 Not sure if needed, but can't hurt */
2014 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2016 /* Setup translations so piece colors match square colors */
2017 symbols[0].name = "light_piece";
2018 symbols[0].value = appData.whitePieceColor;
2019 symbols[1].name = "dark_piece";
2020 symbols[1].value = appData.blackPieceColor;
2021 symbols[2].name = "light_square";
2022 symbols[2].value = appData.lightSquareColor;
2023 symbols[3].name = "dark_square";
2024 symbols[3].value = appData.darkSquareColor;
2026 attr.valuemask = XpmColorSymbols;
2027 attr.colorsymbols = symbols;
2028 attr.numsymbols = 4;
2030 if (appData.monoMode) {
2031 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2035 if (strlen(appData.pixmapDirectory) == 0) {
2036 XpmPieces* pieces = builtInXpms;
2039 while (pieces->size != squareSize && pieces->size) pieces++;
2040 if (!pieces->size) {
2041 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2044 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2045 for (kind=0; kind<4; kind++) {
2047 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2048 pieces->xpm[piece][kind],
2049 &(xpmPieceBitmap2[kind][piece]),
2050 NULL, &attr)) != 0) {
2051 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2055 if(piece <= (int) WhiteKing)
2056 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2060 xpmJailSquare = xpmLightSquare;
2064 fprintf(stderr, _("\nLoading XPMs...\n"));
2067 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2068 fprintf(stderr, "%d ", piece+1);
2069 for (kind=0; kind<4; kind++) {
2070 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2071 ExpandPathName(appData.pixmapDirectory),
2072 piece > (int) WhiteKing ? "w" : "",
2073 pieceBitmapNames[piece],
2075 if (appData.debugMode) {
2076 fprintf(stderr, _("(File:%s:) "), buf);
2078 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2079 &(xpmPieceBitmap2[kind][piece]),
2080 NULL, &attr)) != 0) {
2081 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2082 // [HGM] missing: read of unorthodox piece failed; substitute King.
2083 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2084 ExpandPathName(appData.pixmapDirectory),
2086 if (appData.debugMode) {
2087 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2089 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2090 &(xpmPieceBitmap2[kind][piece]),
2094 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2099 if(piece <= (int) WhiteKing)
2100 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2103 /* Load light and dark squares */
2104 /* If the LSQ and DSQ pieces don't exist, we will
2105 draw them with solid squares. */
2106 fprintf(stderr, _("light square "));
2107 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2108 if (access(buf, 0) != 0) {
2112 if (appData.debugMode)
2113 fprintf(stderr, _("(File:%s:) "), buf);
2115 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2116 &xpmLightSquare, NULL, &attr)) != 0) {
2117 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2120 fprintf(stderr, _("dark square "));
2121 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2122 ExpandPathName(appData.pixmapDirectory), ss);
2123 if (appData.debugMode) {
2124 fprintf(stderr, _("(File:%s:) "), buf);
2126 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2127 &xpmDarkSquare, NULL, &attr)) != 0) {
2128 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2132 xpmJailSquare = xpmLightSquare;
2133 fprintf(stderr, _("Done.\n"));
2135 oldVariant = -1; // kludge to force re-makig of animation masks
2136 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2139 #endif /* HAVE_LIBXPM */
2142 /* No built-in bitmaps */
2147 u_int ss = squareSize;
2149 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2152 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2153 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2154 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2155 pieceBitmapNames[piece],
2156 ss, kind == SOLID ? 's' : 'o');
2157 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2158 if(piece <= (int)WhiteKing)
2159 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2163 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2167 /* With built-in bitmaps */
2171 BuiltInBits* bib = builtInBits;
2174 u_int ss = squareSize;
2176 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2179 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2181 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2182 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2183 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2184 pieceBitmapNames[piece],
2185 ss, kind == SOLID ? 's' : 'o');
2186 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2187 bib->bits[kind][piece], ss, ss);
2188 if(piece <= (int)WhiteKing)
2189 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2193 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2199 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2204 char msg[MSG_SIZ], fullname[MSG_SIZ];
2206 if (*appData.bitmapDirectory != NULLCHAR) {
2207 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2208 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2209 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2210 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2211 &w, &h, pm, &x_hot, &y_hot);
2212 fprintf(stderr, "load %s\n", name);
2213 if (errcode != BitmapSuccess) {
2215 case BitmapOpenFailed:
2216 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2218 case BitmapFileInvalid:
2219 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2221 case BitmapNoMemory:
2222 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2226 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2230 fprintf(stderr, _("%s: %s...using built-in\n"),
2232 } else if (w != wreq || h != hreq) {
2234 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2235 programName, fullname, w, h, wreq, hreq);
2241 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2251 if (lineGap == 0) return;
2253 /* [HR] Split this into 2 loops for non-square boards. */
2255 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2256 gridSegments[i].x1 = 0;
2257 gridSegments[i].x2 =
2258 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2259 gridSegments[i].y1 = gridSegments[i].y2
2260 = lineGap / 2 + (i * (squareSize + lineGap));
2263 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2264 gridSegments[j + i].y1 = 0;
2265 gridSegments[j + i].y2 =
2266 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2267 gridSegments[j + i].x1 = gridSegments[j + i].x2
2268 = lineGap / 2 + (j * (squareSize + lineGap));
2273 MarkMenuItem (char *menuRef, int state)
2275 MenuItem *item = MenuNameToItem(menuRef);
2279 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2280 XtSetValues(item->handle, args, 1);
2285 EnableMenuItem (char *menuRef, int state)
2287 MenuItem *item = MenuNameToItem(menuRef);
2289 if(item) XtSetSensitive(item->handle, state);
2293 EnableButtonBar (int state)
2295 XtSetSensitive(optList[15].handle, state);
2300 SetMenuEnables (Enables *enab)
2302 while (enab->name != NULL) {
2303 EnableMenuItem(enab->name, enab->value);
2309 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2310 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2314 if(*nprms == 0) return;
2315 item = MenuNameToItem(prms[0]);
2316 if(item) ((MenuProc *) item->proc) ();
2320 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2322 MenuProc *proc = (MenuProc *) addr;
2328 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2330 RecentEngineEvent((int) (intptr_t) addr);
2334 AppendMenuItem (char *msg, int n)
2336 CreateMenuItem((Widget) optList[6].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2348 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2349 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2350 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2351 dmEnables[i].piece);
2352 XtSetSensitive(entry, p != NULL || !appData.testLegality
2353 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2354 && !appData.icsActive));
2356 while (p && *p++ == dmEnables[i].piece) count++;
2357 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2359 XtSetArg(args[j], XtNlabel, label); j++;
2360 XtSetValues(entry, args, j);
2366 do_flash_delay (unsigned long msec)
2372 DrawBorder (int x, int y, int type)
2376 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
2378 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
2379 squareSize+lineGap, squareSize+lineGap);
2383 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2385 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2386 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2388 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2389 if(textureW[kind] < W*squareSize)
2390 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2392 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2393 if(textureH[kind] < H*squareSize)
2394 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2396 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2401 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2402 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2404 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2405 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2406 squareSize, squareSize, x*fac, y*fac);
2408 if (useImages && useImageSqs) {
2412 pm = xpmLightSquare;
2417 case 2: /* neutral */
2419 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2422 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2423 squareSize, squareSize, x*fac, y*fac);
2433 case 2: /* neutral */
2438 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2443 I split out the routines to draw a piece so that I could
2444 make a generic flash routine.
2447 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2449 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2450 switch (square_color) {
2452 case 2: /* neutral */
2454 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2455 ? *pieceToOutline(piece)
2456 : *pieceToSolid(piece),
2457 dest, bwPieceGC, 0, 0,
2458 squareSize, squareSize, x, y);
2461 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2462 ? *pieceToSolid(piece)
2463 : *pieceToOutline(piece),
2464 dest, wbPieceGC, 0, 0,
2465 squareSize, squareSize, x, y);
2471 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2473 switch (square_color) {
2475 case 2: /* neutral */
2477 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2478 ? *pieceToOutline(piece)
2479 : *pieceToSolid(piece),
2480 dest, bwPieceGC, 0, 0,
2481 squareSize, squareSize, x, y, 1);
2484 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2485 ? *pieceToSolid(piece)
2486 : *pieceToOutline(piece),
2487 dest, wbPieceGC, 0, 0,
2488 squareSize, squareSize, x, y, 1);
2494 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2496 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2497 switch (square_color) {
2499 XCopyPlane(xDisplay, *pieceToSolid(piece),
2500 dest, (int) piece < (int) BlackPawn
2501 ? wlPieceGC : blPieceGC, 0, 0,
2502 squareSize, squareSize, x, y, 1);
2505 XCopyPlane(xDisplay, *pieceToSolid(piece),
2506 dest, (int) piece < (int) BlackPawn
2507 ? wdPieceGC : bdPieceGC, 0, 0,
2508 squareSize, squareSize, x, y, 1);
2510 case 2: /* neutral */
2512 break; // should never contain pieces
2517 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2519 int kind, p = piece;
2521 switch (square_color) {
2523 case 2: /* neutral */
2525 if ((int)piece < (int) BlackPawn) {
2533 if ((int)piece < (int) BlackPawn) {
2541 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2542 if(useTexture & square_color+1) {
2543 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2544 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2545 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2546 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2547 XSetClipMask(xDisplay, wlPieceGC, None);
2548 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2550 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2551 dest, wlPieceGC, 0, 0,
2552 squareSize, squareSize, x, y);
2555 typedef void (*DrawFunc)();
2560 if (appData.monoMode) {
2561 if (DefaultDepth(xDisplay, xScreen) == 1) {
2562 return monoDrawPiece_1bit;
2564 return monoDrawPiece;
2568 return colorDrawPieceImage;
2570 return colorDrawPiece;
2575 DrawDot (int marker, int x, int y, int r)
2577 if(appData.monoMode) {
2578 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
2579 x, y, r, r, 0, 64*360);
2580 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
2581 x, y, r, r, 0, 64*360);
2583 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
2584 x, y, r, r, 0, 64*360);
2588 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2589 { // basic front-end board-draw function: takes care of everything that can be in square:
2590 // piece, background, coordinate/count, marker dot
2591 int direction, font_ascent, font_descent;
2592 XCharStruct overall;
2595 if (piece == EmptySquare) {
2596 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2598 drawfunc = ChooseDrawFunc();
2599 drawfunc(piece, square_color, x, y, xBoardWindow);
2602 if(align) { // square carries inscription (coord or piece count)
2604 GC hGC = align < 3 ? coordGC : countGC;
2605 // first calculate where it goes
2606 XTextExtents(countFontStruct, string, 1, &direction,
2607 &font_ascent, &font_descent, &overall);
2609 xx += squareSize - overall.width - 2;
2610 yy += squareSize - font_descent - 1;
2611 } else if (align == 2) {
2612 xx += 2, yy += font_ascent + 1;
2613 } else if (align == 3) {
2614 xx += squareSize - overall.width - 2;
2615 yy += font_ascent + 1;
2616 } else if (align == 4) {
2617 xx += 2, yy += font_ascent + 1;
2620 if (appData.monoMode) {
2621 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2623 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2627 if(marker) { // print fat marker dot, if requested
2628 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2633 FlashDelay (int flash_delay)
2635 XSync(xDisplay, False);
2636 if(flash_delay) do_flash_delay(flash_delay);
2640 Fraction (int x, int start, int stop)
2642 double f = ((double) x - start)/(stop - start);
2643 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2647 static WindowPlacement wpNew;
2650 CoDrag (Widget sh, WindowPlacement *wp)
2653 int j=0, touch=0, fudge = 2;
2654 GetActualPlacement(sh, wp);
2655 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2656 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2657 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2658 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2659 if(!touch ) return; // only windows that touch co-move
2660 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2661 int heightInc = wpNew.height - wpMain.height;
2662 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2663 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2664 wp->y += fracTop * heightInc;
2665 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2666 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2667 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2668 int widthInc = wpNew.width - wpMain.width;
2669 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2670 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2671 wp->y += fracLeft * widthInc;
2672 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2673 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2675 wp->x += wpNew.x - wpMain.x;
2676 wp->y += wpNew.y - wpMain.y;
2677 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2678 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2679 XtSetArg(args[j], XtNx, wp->x); j++;
2680 XtSetArg(args[j], XtNy, wp->y); j++;
2681 XtSetValues(sh, args, j);
2684 static XtIntervalId delayedDragID = 0;
2689 GetActualPlacement(shellWidget, &wpNew);
2690 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2691 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2692 return; // false alarm
2693 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2694 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2695 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2696 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2698 DrawPosition(True, NULL);
2699 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2706 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2708 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2712 EventProc (Widget widget, caddr_t unused, XEvent *event)
2714 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2715 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2718 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
2720 DrawSeekAxis (int x, int y, int xTo, int yTo)
2722 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
2726 DrawSeekBackground (int left, int top, int right, int bottom)
2728 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
2732 DrawSeekText (char *buf, int x, int y)
2734 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
2738 DrawSeekDot (int x, int y, int colorNr)
2740 int square = colorNr & 0x80;
2743 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
2745 XFillRectangle(xDisplay, xBoardWindow, color,
2746 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2748 XFillArc(xDisplay, xBoardWindow, color,
2749 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
2753 DrawGrid (int second)
2755 XDrawSegments(xDisplay, xBoardWindow, lineGC,
2756 second ? secondSegments : // [HGM] dual
2757 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
2762 * event handler for redrawing the board
2765 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2767 DrawPosition(True, NULL);
2772 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
2773 { // [HGM] pv: walk PV
2774 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2777 static int savedIndex; /* gross that this is global */
2780 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
2783 XawTextPosition index, dummy;
2786 XawTextGetSelectionPos(w, &index, &dummy);
2787 XtSetArg(arg, XtNstring, &val);
2788 XtGetValues(w, &arg, 1);
2789 ReplaceComment(savedIndex, val);
2790 if(savedIndex != currentMove) ToNrEvent(savedIndex);
2791 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
2795 EditCommentPopUp (int index, char *title, char *text)
2798 if (text == NULL) text = "";
2799 NewCommentPopup(title, text, index);
2803 CommentPopUp (char *title, char *text)
2805 savedIndex = currentMove; // [HGM] vari
2806 NewCommentPopup(title, text, currentMove);
2812 PopDown(CommentDlg);
2815 static char *openName;
2821 (void) (*fileProc)(openFP, 0, openName);
2825 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
2827 fileProc = proc; /* I can't see a way not */
2828 fileOpenMode = openMode; /* to use globals here */
2829 { // [HGM] use file-selector dialog stolen from Ghostview
2830 int index; // this is not supported yet
2831 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
2832 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
2833 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
2834 ScheduleDelayedEvent(&DelayedLoad, 50);
2839 /* Disable all user input other than deleting the window */
2840 static int frozen = 0;
2846 /* Grab by a widget that doesn't accept input */
2847 XtAddGrab(optList[14].handle, TRUE, FALSE);
2851 /* Undo a FreezeUI */
2855 if (!frozen) return;
2856 XtRemoveGrab(optList[14].handle);
2864 static int oldPausing = FALSE;
2865 static GameMode oldmode = (GameMode) -1;
2868 if (!boardWidget || !XtIsRealized(boardWidget)) return;
2870 if (pausing != oldPausing) {
2871 oldPausing = pausing;
2872 MarkMenuItem("Mode.Pause", pausing);
2874 if (appData.showButtonBar) {
2875 /* Always toggle, don't set. Previous code messes up when
2876 invoked while the button is pressed, as releasing it
2877 toggles the state again. */
2880 XtSetArg(args[0], XtNbackground, &oldbg);
2881 XtSetArg(args[1], XtNforeground, &oldfg);
2882 XtGetValues(optList[18].handle,
2884 XtSetArg(args[0], XtNbackground, oldfg);
2885 XtSetArg(args[1], XtNforeground, oldbg);
2887 XtSetValues(optList[18].handle, args, 2);
2891 wname = ModeToWidgetName(oldmode);
2892 if (wname != NULL) {
2893 MarkMenuItem(wname, False);
2895 wname = ModeToWidgetName(gameMode);
2896 if (wname != NULL) {
2897 MarkMenuItem(wname, True);
2900 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
2902 /* Maybe all the enables should be handled here, not just this one */
2903 EnableMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
2908 * Button/menu procedures
2911 /* this variable is shared between CopyPositionProc and SendPositionSelection */
2912 char *selected_fen_position=NULL;
2915 SendPositionSelection (Widget w, Atom *selection, Atom *target,
2916 Atom *type_return, XtPointer *value_return,
2917 unsigned long *length_return, int *format_return)
2919 char *selection_tmp;
2921 // if (!selected_fen_position) return False; /* should never happen */
2922 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
2923 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
2924 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
2927 if (f == NULL) return False;
2931 selection_tmp = XtMalloc(len + 1);
2932 count = fread(selection_tmp, 1, len, f);
2935 XtFree(selection_tmp);
2938 selection_tmp[len] = NULLCHAR;
2940 /* note: since no XtSelectionDoneProc was registered, Xt will
2941 * automatically call XtFree on the value returned. So have to
2942 * make a copy of it allocated with XtMalloc */
2943 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
2944 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
2947 *value_return=selection_tmp;
2948 *length_return=strlen(selection_tmp);
2949 *type_return=*target;
2950 *format_return = 8; /* bits per byte */
2952 } else if (*target == XA_TARGETS(xDisplay)) {
2953 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
2954 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
2955 targets_tmp[1] = XA_STRING;
2956 *value_return = targets_tmp;
2957 *type_return = XA_ATOM;
2960 // This code leads to a read of value_return out of bounds on 64-bit systems.
2961 // Other code which I have seen always sets *format_return to 32 independent of
2962 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
2963 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
2964 *format_return = 8 * sizeof(Atom);
2965 if (*format_return > 32) {
2966 *length_return *= *format_return / 32;
2967 *format_return = 32;
2970 *format_return = 32;
2978 /* note: when called from menu all parameters are NULL, so no clue what the
2979 * Widget which was clicked on was, or what the click event was
2982 CopySomething (char *src)
2984 selected_fen_position = src;
2986 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2987 * have a notion of a position that is selected but not copied.
2988 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2990 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2992 SendPositionSelection,
2993 NULL/* lose_ownership_proc */ ,
2994 NULL/* transfer_done_proc */);
2995 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2997 SendPositionSelection,
2998 NULL/* lose_ownership_proc */ ,
2999 NULL/* transfer_done_proc */);
3002 /* function called when the data to Paste is ready */
3004 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3005 Atom *type, XtPointer value, unsigned long *len, int *format)
3008 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3009 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3010 EditPositionPasteFEN(fenstr);
3014 /* called when Paste Position button is pressed,
3015 * all parameters will be NULL */
3017 PastePositionProc ()
3019 XtGetSelectionValue(menuBarWidget,
3020 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3021 /* (XtSelectionCallbackProc) */ PastePositionCB,
3022 NULL, /* client_data passed to PastePositionCB */
3024 /* better to use the time field from the event that triggered the
3025 * call to this function, but that isn't trivial to get
3032 /* note: when called from menu all parameters are NULL, so no clue what the
3033 * Widget which was clicked on was, or what the click event was
3035 /* function called when the data to Paste is ready */
3037 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3038 Atom *type, XtPointer value, unsigned long *len, int *format)
3041 if (value == NULL || *len == 0) {
3042 return; /* nothing had been selected to copy */
3044 f = fopen(gamePasteFilename, "w");
3046 DisplayError(_("Can't open temp file"), errno);
3049 fwrite(value, 1, *len, f);
3052 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3055 /* called when Paste Game button is pressed,
3056 * all parameters will be NULL */
3060 XtGetSelectionValue(menuBarWidget,
3061 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3062 /* (XtSelectionCallbackProc) */ PasteGameCB,
3063 NULL, /* client_data passed to PasteGameCB */
3065 /* better to use the time field from the event that triggered the
3066 * call to this function, but that isn't trivial to get
3075 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3082 { // bassic primitive for determining if modifier keys are pressed
3083 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3086 XQueryKeymap(xDisplay,keys);
3087 for(i=0; i<6; i++) {
3089 j = XKeysymToKeycode(xDisplay, codes[i]);
3090 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3096 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3100 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3101 if ( n == 1 && *buf >= 32 // printable
3102 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3103 ) BoxAutoPopUp (buf);
3107 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3108 { // [HGM] input: let up-arrow recall previous line from history
3113 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3114 { // [HGM] input: let down-arrow recall next line from history
3119 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3125 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3127 if (!TempBackwardActive) {
3128 TempBackwardActive = True;
3134 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3136 /* Check to see if triggered by a key release event for a repeating key.
3137 * If so the next queued event will be a key press of the same key at the same time */
3138 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3140 XPeekEvent(xDisplay, &next);
3141 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3142 next.xkey.keycode == event->xkey.keycode)
3146 TempBackwardActive = False;
3150 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3151 { // called as key binding
3154 if (nprms && *nprms > 0)
3158 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3163 SetWindowTitle (char *text, char *title, char *icon)
3167 if (appData.titleInWindow) {
3169 XtSetArg(args[i], XtNlabel, text); i++;
3170 XtSetValues(titleWidget, args, i);
3173 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3174 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3175 XtSetValues(shellWidget, args, i);
3176 XSync(xDisplay, False);
3181 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3187 DisplayIcsInteractionTitle (String message)
3189 if (oldICSInteractionTitle == NULL) {
3190 /* Magic to find the old window title, adapted from vim */
3191 char *wina = getenv("WINDOWID");
3193 Window win = (Window) atoi(wina);
3194 Window root, parent, *children;
3195 unsigned int nchildren;
3196 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3198 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3199 if (!XQueryTree(xDisplay, win, &root, &parent,
3200 &children, &nchildren)) break;
3201 if (children) XFree((void *)children);
3202 if (parent == root || parent == 0) break;
3205 XSetErrorHandler(oldHandler);
3207 if (oldICSInteractionTitle == NULL) {
3208 oldICSInteractionTitle = "xterm";
3211 printf("\033]0;%s\007", message);
3216 XtIntervalId delayedEventTimerXID = 0;
3217 DelayedEventCallback delayedEventCallback = 0;
3222 delayedEventTimerXID = 0;
3223 delayedEventCallback();
3227 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3229 if(delayedEventTimerXID && delayedEventCallback == cb)
3230 // [HGM] alive: replace, rather than add or flush identical event
3231 XtRemoveTimeOut(delayedEventTimerXID);
3232 delayedEventCallback = cb;
3233 delayedEventTimerXID =
3234 XtAppAddTimeOut(appContext, millisec,
3235 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3238 DelayedEventCallback
3241 if (delayedEventTimerXID) {
3242 return delayedEventCallback;
3249 CancelDelayedEvent ()
3251 if (delayedEventTimerXID) {
3252 XtRemoveTimeOut(delayedEventTimerXID);
3253 delayedEventTimerXID = 0;
3257 XtIntervalId loadGameTimerXID = 0;
3260 LoadGameTimerRunning ()
3262 return loadGameTimerXID != 0;
3266 StopLoadGameTimer ()
3268 if (loadGameTimerXID != 0) {
3269 XtRemoveTimeOut(loadGameTimerXID);
3270 loadGameTimerXID = 0;
3278 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3280 loadGameTimerXID = 0;
3285 StartLoadGameTimer (long millisec)
3288 XtAppAddTimeOut(appContext, millisec,
3289 (XtTimerCallbackProc) LoadGameTimerCallback,
3293 XtIntervalId analysisClockXID = 0;
3296 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3298 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3299 || appData.icsEngineAnalyze) { // [DM]
3300 AnalysisPeriodicEvent(0);
3301 StartAnalysisClock();
3306 StartAnalysisClock ()
3309 XtAppAddTimeOut(appContext, 2000,
3310 (XtTimerCallbackProc) AnalysisClockCallback,
3314 XtIntervalId clockTimerXID = 0;
3317 ClockTimerRunning ()
3319 return clockTimerXID != 0;
3325 if (clockTimerXID != 0) {
3326 XtRemoveTimeOut(clockTimerXID);
3335 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3342 StartClockTimer (long millisec)
3345 XtAppAddTimeOut(appContext, millisec,
3346 (XtTimerCallbackProc) ClockTimerCallback,
3351 DisplayTimerLabel (int optNr, char *color, long timer, int highlight)
3355 Widget w = optList[optNr].handle;
3357 /* check for low time warning */
3358 Pixel foregroundOrWarningColor = timerForegroundPixel;
3361 appData.lowTimeWarning &&
3362 (timer / 1000) < appData.icsAlarmTime)
3363 foregroundOrWarningColor = lowTimeWarningColor;
3365 if (appData.clockMode) {
3366 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
3367 XtSetArg(args[0], XtNlabel, buf);
3369 snprintf(buf, MSG_SIZ, "%s ", color);
3370 XtSetArg(args[0], XtNlabel, buf);
3375 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3376 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3378 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3379 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3382 XtSetValues(w, args, 3);
3385 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3388 SetClockIcon (int color)
3391 Pixmap pm = *clockIcons[color];
3392 if (iconPixmap != pm) {
3394 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3395 XtSetValues(shellWidget, args, 1);
3400 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3402 InputSource *is = (InputSource *) closure;
3407 if (is->lineByLine) {
3408 count = read(is->fd, is->unused,
3409 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3411 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3414 is->unused += count;
3416 while (p < is->unused) {
3417 q = memchr(p, '\n', is->unused - p);
3418 if (q == NULL) break;
3420 (is->func)(is, is->closure, p, q - p, 0);
3424 while (p < is->unused) {
3429 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3434 (is->func)(is, is->closure, is->buf, count, error);
3439 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3442 ChildProc *cp = (ChildProc *) pr;
3444 is = (InputSource *) calloc(1, sizeof(InputSource));
3445 is->lineByLine = lineByLine;
3449 is->fd = fileno(stdin);
3451 is->kind = cp->kind;
3452 is->fd = cp->fdFrom;
3455 is->unused = is->buf;
3458 is->xid = XtAppAddInput(appContext, is->fd,
3459 (XtPointer) (XtInputReadMask),
3460 (XtInputCallbackProc) DoInputCallback,
3462 is->closure = closure;
3463 return (InputSourceRef) is;
3467 RemoveInputSource (InputSourceRef isr)
3469 InputSource *is = (InputSource *) isr;
3471 if (is->xid == 0) return;
3472 XtRemoveInput(is->xid);
3476 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3478 /* Masks for XPM pieces. Black and white pieces can have
3479 different shapes, but in the interest of retaining my
3480 sanity pieces must have the same outline on both light
3481 and dark squares, and all pieces must use the same
3482 background square colors/images. */
3484 static int xpmDone = 0;
3485 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3486 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3489 CreateAnimMasks (int pieceDepth)
3495 unsigned long plane;
3498 /* Need a bitmap just to get a GC with right depth */
3499 buf = XCreatePixmap(xDisplay, xBoardWindow,
3501 values.foreground = 1;
3502 values.background = 0;
3503 /* Don't use XtGetGC, not read only */
3504 maskGC = XCreateGC(xDisplay, buf,
3505 GCForeground | GCBackground, &values);
3506 XFreePixmap(xDisplay, buf);
3508 buf = XCreatePixmap(xDisplay, xBoardWindow,
3509 squareSize, squareSize, pieceDepth);
3510 values.foreground = XBlackPixel(xDisplay, xScreen);
3511 values.background = XWhitePixel(xDisplay, xScreen);
3512 bufGC = XCreateGC(xDisplay, buf,
3513 GCForeground | GCBackground, &values);
3515 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3516 /* Begin with empty mask */
3517 if(!xpmDone) // [HGM] pieces: keep using existing
3518 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3519 squareSize, squareSize, 1);
3520 XSetFunction(xDisplay, maskGC, GXclear);
3521 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3522 0, 0, squareSize, squareSize);
3524 /* Take a copy of the piece */
3529 XSetFunction(xDisplay, bufGC, GXcopy);
3530 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3532 0, 0, squareSize, squareSize, 0, 0);
3534 /* XOR the background (light) over the piece */
3535 XSetFunction(xDisplay, bufGC, GXxor);
3537 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3538 0, 0, squareSize, squareSize, 0, 0);
3540 XSetForeground(xDisplay, bufGC, lightSquareColor);
3541 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3544 /* We now have an inverted piece image with the background
3545 erased. Construct mask by just selecting all the non-zero
3546 pixels - no need to reconstruct the original image. */
3547 XSetFunction(xDisplay, maskGC, GXor);
3549 /* Might be quicker to download an XImage and create bitmap
3550 data from it rather than this N copies per piece, but it
3551 only takes a fraction of a second and there is a much
3552 longer delay for loading the pieces. */
3553 for (n = 0; n < pieceDepth; n ++) {
3554 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3555 0, 0, squareSize, squareSize,
3561 XFreePixmap(xDisplay, buf);
3562 XFreeGC(xDisplay, bufGC);
3563 XFreeGC(xDisplay, maskGC);
3567 InitAnimState (AnimNr anr, XWindowAttributes *info)
3572 /* Each buffer is square size, same depth as window */
3573 animBufs[anr+4] = xBoardWindow;
3574 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3575 squareSize, squareSize, info->depth);
3576 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3577 squareSize, squareSize, info->depth);
3579 /* Create a plain GC for blitting */
3580 mask = GCForeground | GCBackground | GCFunction |
3581 GCPlaneMask | GCGraphicsExposures;
3582 values.foreground = XBlackPixel(xDisplay, xScreen);
3583 values.background = XWhitePixel(xDisplay, xScreen);
3584 values.function = GXcopy;
3585 values.plane_mask = AllPlanes;
3586 values.graphics_exposures = False;
3587 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3589 /* Piece will be copied from an existing context at
3590 the start of each new animation/drag. */
3591 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3593 /* Outline will be a read-only copy of an existing */
3594 animGCs[anr+4] = None;
3600 XWindowAttributes info;
3602 if (xpmDone && gameInfo.variant == oldVariant) return;
3603 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3604 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3606 InitAnimState(Game, &info);
3607 InitAnimState(Player, &info);
3609 /* For XPM pieces, we need bitmaps to use as masks. */
3611 CreateAnimMasks(info.depth), xpmDone = 1;
3616 static Boolean frameWaiting;
3619 FrameAlarm (int sig)
3621 frameWaiting = False;
3622 /* In case System-V style signals. Needed?? */
3623 signal(SIGALRM, FrameAlarm);
3627 FrameDelay (int time)
3629 struct itimerval delay;
3631 XSync(xDisplay, False);
3634 frameWaiting = True;
3635 signal(SIGALRM, FrameAlarm);
3636 delay.it_interval.tv_sec =
3637 delay.it_value.tv_sec = time / 1000;
3638 delay.it_interval.tv_usec =
3639 delay.it_value.tv_usec = (time % 1000) * 1000;
3640 setitimer(ITIMER_REAL, &delay, NULL);
3641 while (frameWaiting) pause();
3642 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3643 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3644 setitimer(ITIMER_REAL, &delay, NULL);
3651 FrameDelay (int time)
3653 XSync(xDisplay, False);
3655 usleep(time * 1000);
3661 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3665 /* Bitmap for piece being moved. */
3666 if (appData.monoMode) {
3667 *mask = *pieceToSolid(piece);
3668 } else if (useImages) {
3670 *mask = xpmMask[piece];
3672 *mask = ximMaskPm[piece];
3675 *mask = *pieceToSolid(piece);
3678 /* GC for piece being moved. Square color doesn't matter, but
3679 since it gets modified we make a copy of the original. */
3681 if (appData.monoMode)
3686 if (appData.monoMode)
3691 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3693 /* Outline only used in mono mode and is not modified */
3695 *outline = bwPieceGC;
3697 *outline = wbPieceGC;
3701 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
3706 /* Draw solid rectangle which will be clipped to shape of piece */
3707 XFillRectangle(xDisplay, dest, clip,
3708 0, 0, squareSize, squareSize);
3709 if (appData.monoMode)
3710 /* Also draw outline in contrasting color for black
3711 on black / white on white cases */
3712 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3713 0, 0, squareSize, squareSize, 0, 0, 1);
3715 /* Copy the piece */
3720 if(appData.upsideDown && flipView) kind ^= 2;
3721 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3723 0, 0, squareSize, squareSize,
3729 InsertPiece (AnimNr anr, ChessSquare piece)
3731 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
3735 DrawBlank (AnimNr anr, int x, int y, int startColor)
3737 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
3740 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
3741 int srcX, int srcY, int width, int height, int destX, int destY)
3743 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
3744 srcX, srcY, width, height, destX, destY);
3748 SetDragPiece (AnimNr anr, ChessSquare piece)
3751 /* The piece will be drawn using its own bitmap as a matte */
3752 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
3753 XSetClipMask(xDisplay, animGCs[anr+2], mask);
3756 /* [AS] Arrow highlighting support */
3759 DrawPolygon (Pnt arrow[], int nr)
3763 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
3764 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
3765 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
3769 UpdateLogos (int displ)
3771 return; // no logos in XBoard yet