2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
64 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.h>
68 # if HAVE_SYS_SOCKET_H
69 # include <sys/socket.h>
70 # include <netinet/in.h>
72 # else /* not HAVE_SYS_SOCKET_H */
73 # if HAVE_LAN_SOCKET_H
74 # include <lan/socket.h>
76 # include <lan/netdb.h>
77 # else /* not HAVE_LAN_SOCKET_H */
78 # define OMIT_SOCKETS 1
79 # endif /* not HAVE_LAN_SOCKET_H */
80 # endif /* not HAVE_SYS_SOCKET_H */
81 #endif /* !OMIT_SOCKETS */
86 #else /* not STDC_HEADERS */
87 extern char *getenv();
90 # else /* not HAVE_STRING_H */
92 # endif /* not HAVE_STRING_H */
93 #endif /* not STDC_HEADERS */
96 # include <sys/fcntl.h>
97 #else /* not HAVE_SYS_FCNTL_H */
100 # endif /* HAVE_FCNTL_H */
101 #endif /* not HAVE_SYS_FCNTL_H */
103 #if HAVE_SYS_SYSTEMINFO_H
104 # include <sys/systeminfo.h>
105 #endif /* HAVE_SYS_SYSTEMINFO_H */
107 #if TIME_WITH_SYS_TIME
108 # include <sys/time.h>
112 # include <sys/time.h>
123 # include <sys/wait.h>
128 # define NAMLEN(dirent) strlen((dirent)->d_name)
129 # define HAVE_DIR_STRUCT
131 # define dirent direct
132 # define NAMLEN(dirent) (dirent)->d_namlen
134 # include <sys/ndir.h>
135 # define HAVE_DIR_STRUCT
138 # include <sys/dir.h>
139 # define HAVE_DIR_STRUCT
143 # define HAVE_DIR_STRUCT
151 #include <X11/Intrinsic.h>
152 #include <X11/StringDefs.h>
153 #include <X11/Shell.h>
154 #include <X11/cursorfont.h>
155 #include <X11/Xatom.h>
156 #include <X11/Xmu/Atoms.h>
158 #include <X11/Xaw3d/Dialog.h>
159 #include <X11/Xaw3d/Form.h>
160 #include <X11/Xaw3d/List.h>
161 #include <X11/Xaw3d/Label.h>
162 #include <X11/Xaw3d/SimpleMenu.h>
163 #include <X11/Xaw3d/SmeBSB.h>
164 #include <X11/Xaw3d/SmeLine.h>
165 #include <X11/Xaw3d/Box.h>
166 #include <X11/Xaw3d/MenuButton.h>
167 #include <X11/Xaw3d/Text.h>
168 #include <X11/Xaw3d/AsciiText.h>
170 #include <X11/Xaw/Dialog.h>
171 #include <X11/Xaw/Form.h>
172 #include <X11/Xaw/List.h>
173 #include <X11/Xaw/Label.h>
174 #include <X11/Xaw/SimpleMenu.h>
175 #include <X11/Xaw/SmeBSB.h>
176 #include <X11/Xaw/SmeLine.h>
177 #include <X11/Xaw/Box.h>
178 #include <X11/Xaw/MenuButton.h>
179 #include <X11/Xaw/Text.h>
180 #include <X11/Xaw/AsciiText.h>
183 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
188 #include "pixmaps/pixmaps.h"
189 #define IMAGE_EXT "xpm"
191 #define IMAGE_EXT "xim"
192 #include "bitmaps/bitmaps.h"
195 #include "bitmaps/icon_white.bm"
196 #include "bitmaps/icon_black.bm"
197 #include "bitmaps/checkmark.bm"
199 #include "frontend.h"
201 #include "backendz.h"
205 #include "xgamelist.h"
206 #include "xhistory.h"
207 #include "xevalgraph.h"
208 #include "xedittags.h"
212 #include "engineoutput.h"
221 #define usleep(t) _sleep2(((t)+500)/1000)
225 # define _(s) gettext (s)
226 # define N_(s) gettext_noop (s)
232 int main P((int argc, char **argv));
233 RETSIGTYPE CmailSigHandler P((int sig));
234 RETSIGTYPE IntSigHandler P((int sig));
235 RETSIGTYPE TermSizeSigHandler P((int sig));
236 static void CreateGCs P((int redo));
237 static void CreateAnyPieces P((void));
238 void CreateXIMPieces P((void));
239 void CreateXPMPieces P((void));
240 void CreatePNGPieces P((void));
241 void CreateXPMBoard P((char *s, int n));
242 void CreatePieces P((void));
243 Widget CreateMenuBar P((Menu *mb, int boardWidth));
245 char *InsertPxlSize P((char *pattern, int targetPxlSize));
246 XFontSet CreateFontSet P((char *base_fnt_lst));
248 char *FindFont P((char *pattern, int targetPxlSize));
250 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
251 u_int wreq, u_int hreq));
252 void CreateGrid P((void));
253 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
254 void DelayedDrag P((void));
255 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
256 void HandlePV P((Widget w, XEvent * event,
257 String * params, Cardinal * nParams));
258 void DrawPositionProc P((Widget w, XEvent *event,
259 String *prms, Cardinal *nprms));
260 void CommentClick P((Widget w, XEvent * event,
261 String * params, Cardinal * nParams));
262 void ICSInputBoxPopUp P((void));
263 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
264 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
265 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
266 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
267 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
268 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
269 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
270 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
271 Boolean TempBackwardActive = False;
272 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
273 void DisplayMove P((int moveNumber));
274 void ICSInitScript P((void));
275 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
276 void update_ics_width P(());
277 int CopyMemoProc P(());
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 gridSegments[BOARD_RANKS + BOARD_FILES + 2];
298 XFontSet fontSet, clockFontSet;
301 XFontStruct *clockFontStruct;
303 Font coordFontID, countFontID;
304 XFontStruct *coordFontStruct, *countFontStruct;
305 XtAppContext appContext;
308 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
310 Position commentX = -1, commentY = -1;
311 Dimension commentW, commentH;
312 typedef unsigned int BoardSize;
314 Boolean chessProgram;
316 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
317 int smallLayout = 0, tinyLayout = 0,
318 marginW, marginH, // [HGM] for run-time resizing
319 fromX = -1, fromY = -1, toX, toY, commentUp = False,
320 errorExitStatus = -1, defaultLineGap;
321 Dimension textHeight;
322 Pixel timerForegroundPixel, timerBackgroundPixel;
323 Pixel buttonForegroundPixel, buttonBackgroundPixel;
324 char *chessDir, *programName, *programVersion;
325 Boolean alwaysOnTop = False;
326 char *icsTextMenuString;
328 char *firstChessProgramNames;
329 char *secondChessProgramNames;
331 WindowPlacement wpMain;
332 WindowPlacement wpConsole;
333 WindowPlacement wpComment;
334 WindowPlacement wpMoveHistory;
335 WindowPlacement wpEvalGraph;
336 WindowPlacement wpEngineOutput;
337 WindowPlacement wpGameList;
338 WindowPlacement wpTags;
343 Boolean cairoAnimate;
344 static cairo_surface_t *csBoardWindow, *csBoardBackup, *csDualBoard;
345 static cairo_surface_t *pngPieceBitmaps[2][(int)BlackPawn]; // scaled pieces as used
346 static cairo_surface_t *pngPieceBitmaps2[2][(int)BlackPawn+4]; // scaled pieces in store
347 static cairo_surface_t *pngBoardBitmap[2];
348 Pixmap pieceBitmap[2][(int)BlackPawn];
349 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
350 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
351 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
352 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
353 Pixmap xpmBoardBitmap[2];
354 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
355 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
356 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
357 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
358 XImage *ximLightSquare, *ximDarkSquare;
361 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
362 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
364 #define White(piece) ((int)(piece) < (int)BlackPawn)
366 /* Bitmaps for use as masks when drawing XPM pieces.
367 Need one for each black and white piece. */
368 static Pixmap xpmMask[BlackKing + 1];
370 /* This magic number is the number of intermediate frames used
371 in each half of the animation. For short moves it's reduced
372 by 1. The total number of frames will be factor * 2 + 1. */
375 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
382 DropMenuEnables dmEnables[] = {
399 XtResource clientResources[] = {
400 { "flashCount", "flashCount", XtRInt, sizeof(int),
401 XtOffset(AppDataPtr, flashCount), XtRImmediate,
402 (XtPointer) FLASH_COUNT },
405 XrmOptionDescRec shellOptions[] = {
406 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
407 { "-flash", "flashCount", XrmoptionNoArg, "3" },
408 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
411 XtActionsRec boardActions[] = {
412 { "DrawPosition", DrawPositionProc },
413 { "HandlePV", HandlePV },
414 { "SelectPV", SelectPV },
415 { "StopPV", StopPV },
416 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
417 { "QuitProc", QuitWrapper },
418 { "ManProc", ManInner },
419 { "TempBackwardProc", TempBackwardProc },
420 { "TempForwardProc", TempForwardProc },
421 { "CommentClick", (XtActionProc) CommentClick },
422 { "GenericPopDown", (XtActionProc) GenericPopDown },
423 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
424 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
425 { "SelectMove", (XtActionProc) SelectMove },
426 { "LoadSelectedProc", LoadSelectedProc },
427 { "SetFilterProc", SetFilterProc },
428 { "TypeInProc", TypeInProc },
429 { "EnterKeyProc", EnterKeyProc },
430 { "UpKeyProc", UpKeyProc },
431 { "DownKeyProc", DownKeyProc },
432 { "WheelProc", WheelProc },
433 { "TabProc", TabProc },
436 char globalTranslations[] =
437 ":<Key>F9: MenuItem(Actions.Resign) \n \
438 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
439 :Meta<Key>V: MenuItem(File.NewVariant) \n \
440 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
441 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
442 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
443 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
444 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
445 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
446 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
447 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
448 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
449 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
450 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
451 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
452 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
453 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
454 :Ctrl<Key>q: MenuItem(File.Quit) \n \
455 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
456 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
457 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
458 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
459 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
460 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
461 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
462 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
463 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
464 :Meta<Key>G: MenuItem(View.GameList) \n \
465 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
466 :<Key>Pause: MenuItem(Mode.Pause) \n \
467 :<Key>F3: MenuItem(Action.Accept) \n \
468 :<Key>F4: MenuItem(Action.Decline) \n \
469 :<Key>F12: MenuItem(Action.Rematch) \n \
470 :<Key>F5: MenuItem(Action.CallFlag) \n \
471 :<Key>F6: MenuItem(Action.Draw) \n \
472 :<Key>F7: MenuItem(Action.Adjourn) \n \
473 :<Key>F8: MenuItem(Action.Abort) \n \
474 :<Key>F10: MenuItem(Action.StopObserving) \n \
475 :<Key>F11: MenuItem(Action.StopExamining) \n \
476 :Ctrl<Key>d: MenuItem(DebugProc) \n \
477 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
478 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
479 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
480 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
481 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
482 :<Key>Left: MenuItem(Edit.Backward) \n \
483 :<Key>Right: MenuItem(Edit.Forward) \n \
484 :<Key>Home: MenuItem(Edit.Revert) \n \
485 :<Key>End: MenuItem(Edit.TruncateGame) \n \
486 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
487 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
488 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
489 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
490 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
491 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
492 #ifndef OPTIONSDIALOG
494 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
495 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
496 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
497 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
498 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
501 :<Key>F1: MenuItem(Help.ManXBoard) \n \
502 :<Key>F2: MenuItem(View.FlipView) \n \
503 :<KeyDown>Return: TempBackwardProc() \n \
504 :<KeyUp>Return: TempForwardProc() \n";
506 char ICSInputTranslations[] =
507 "<Key>Up: UpKeyProc() \n "
508 "<Key>Down: DownKeyProc() \n "
509 "<Key>Return: EnterKeyProc() \n";
511 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
512 // as the widget is destroyed before the up-click can call extend-end
513 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
515 String xboardResources[] = {
516 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
521 /* Max possible square size */
522 #define MAXSQSIZE 256
524 static int xpm_avail[MAXSQSIZE];
526 #ifdef HAVE_DIR_STRUCT
528 /* Extract piece size from filename */
530 xpm_getsize (char *name, int len, char *ext)
538 if ((p=strchr(name, '.')) == NULL ||
539 StrCaseCmp(p+1, ext) != 0)
545 while (*p && isdigit(*p))
552 /* Setup xpm_avail */
554 xpm_getavail (char *dirname, char *ext)
560 for (i=0; i<MAXSQSIZE; ++i)
563 if (appData.debugMode)
564 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
566 dir = opendir(dirname);
569 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
570 programName, dirname);
574 while ((ent=readdir(dir)) != NULL) {
575 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
576 if (i > 0 && i < MAXSQSIZE)
586 xpm_print_avail (FILE *fp, char *ext)
590 fprintf(fp, _("Available `%s' sizes:\n"), ext);
591 for (i=1; i<MAXSQSIZE; ++i) {
597 /* Return XPM piecesize closest to size */
599 xpm_closest_to (char *dirname, int size, char *ext)
602 int sm_diff = MAXSQSIZE;
606 xpm_getavail(dirname, ext);
608 if (appData.debugMode)
609 xpm_print_avail(stderr, ext);
611 for (i=1; i<MAXSQSIZE; ++i) {
614 diff = (diff<0) ? -diff : diff;
615 if (diff < sm_diff) {
623 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
629 #else /* !HAVE_DIR_STRUCT */
630 /* If we are on a system without a DIR struct, we can't
631 read the directory, so we can't collect a list of
632 filenames, etc., so we can't do any size-fitting. */
634 xpm_closest_to (char *dirname, int size, char *ext)
637 Warning: No DIR structure found on this system --\n\
638 Unable to autosize for XPM/XIM pieces.\n\
639 Please report this error to %s.\n\
640 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
643 #endif /* HAVE_DIR_STRUCT */
646 /* Arrange to catch delete-window events */
647 Atom wm_delete_window;
649 CatchDeleteWindow (Widget w, String procname)
652 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
653 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
654 XtAugmentTranslations(w, XtParseTranslationTable(buf));
661 XtSetArg(args[0], XtNiconic, False);
662 XtSetValues(shellWidget, args, 1);
664 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
667 //---------------------------------------------------------------------------------------------------------
668 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
671 #define CW_USEDEFAULT (1<<31)
672 #define ICS_TEXT_MENU_SIZE 90
673 #define DEBUG_FILE "xboard.debug"
674 #define SetCurrentDirectory chdir
675 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
679 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
682 // front-end part of option handling
684 // [HGM] This platform-dependent table provides the location for storing the color info
685 extern char *crWhite, * crBlack;
689 &appData.whitePieceColor,
690 &appData.blackPieceColor,
691 &appData.lightSquareColor,
692 &appData.darkSquareColor,
693 &appData.highlightSquareColor,
694 &appData.premoveHighlightColor,
695 &appData.lowTimeWarningColor,
706 // [HGM] font: keep a font for each square size, even non-stndard ones
709 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
710 char *fontTable[NUM_FONTS][MAX_SIZE];
713 ParseFont (char *name, int number)
714 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
716 if(sscanf(name, "size%d:", &size)) {
717 // [HGM] font: font is meant for specific boardSize (likely from settings file);
718 // defer processing it until we know if it matches our board size
719 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
720 fontTable[number][size] = strdup(strchr(name, ':')+1);
721 fontValid[number][size] = True;
726 case 0: // CLOCK_FONT
727 appData.clockFont = strdup(name);
729 case 1: // MESSAGE_FONT
730 appData.font = strdup(name);
732 case 2: // COORD_FONT
733 appData.coordFont = strdup(name);
738 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
743 { // only 2 fonts currently
744 appData.clockFont = CLOCK_FONT_NAME;
745 appData.coordFont = COORD_FONT_NAME;
746 appData.font = DEFAULT_FONT_NAME;
751 { // no-op, until we identify the code for this already in XBoard and move it here
755 ParseColor (int n, char *name)
756 { // in XBoard, just copy the color-name string
757 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
761 ParseTextAttribs (ColorClass cc, char *s)
763 (&appData.colorShout)[cc] = strdup(s);
767 ParseBoardSize (void *addr, char *name)
769 appData.boardSize = strdup(name);
774 { // In XBoard the sound-playing program takes care of obtaining the actual sound
778 SetCommPortDefaults ()
779 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
782 // [HGM] args: these three cases taken out to stay in front-end
784 SaveFontArg (FILE *f, ArgDescriptor *ad)
787 int i, n = (int)(intptr_t)ad->argLoc;
789 case 0: // CLOCK_FONT
790 name = appData.clockFont;
792 case 1: // MESSAGE_FONT
795 case 2: // COORD_FONT
796 name = appData.coordFont;
801 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
802 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
803 fontTable[n][squareSize] = strdup(name);
804 fontValid[n][squareSize] = True;
807 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
808 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
813 { // nothing to do, as the sounds are at all times represented by their text-string names already
817 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
818 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
819 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
823 SaveColor (FILE *f, ArgDescriptor *ad)
824 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
825 if(colorVariable[(int)(intptr_t)ad->argLoc])
826 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
830 SaveBoardSize (FILE *f, char *name, void *addr)
831 { // wrapper to shield back-end from BoardSize & sizeInfo
832 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
836 ParseCommPortSettings (char *s)
837 { // no such option in XBoard (yet)
843 GetActualPlacement (Widget wg, WindowPlacement *wp)
845 XWindowAttributes winAt;
852 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
853 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
854 wp->x = rx - winAt.x;
855 wp->y = ry - winAt.y;
856 wp->height = winAt.height;
857 wp->width = winAt.width;
858 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
863 { // wrapper to shield use of window handles from back-end (make addressible by number?)
864 // In XBoard this will have to wait until awareness of window parameters is implemented
865 GetActualPlacement(shellWidget, &wpMain);
866 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
867 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
868 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
869 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
870 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
871 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
875 PrintCommPortSettings (FILE *f, char *name)
876 { // This option does not exist in XBoard
880 EnsureOnScreen (int *x, int *y, int minX, int minY)
887 { // [HGM] args: allows testing if main window is realized from back-end
888 return xBoardWindow != 0;
894 extern Option dualOptions[];
896 Window tmp = xBoardWindow;
897 cairo_surface_t *cstmp = csBoardWindow;
898 if(!dual) dual = XtWindow(dualOptions[3].handle); // must be first call
899 xBoardWindow = dual; // swap them
901 csBoardWindow = csDualBoard;
902 if(!csDualBoard && cstmp) {
903 int boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
904 int boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
905 csBoardWindow = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), boardWidth, boardHeight);
910 PopUpStartupDialog ()
911 { // start menu not implemented in XBoard
915 ConvertToLine (int argc, char **argv)
917 static char line[128*1024], buf[1024];
921 for(i=1; i<argc; i++)
923 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
924 && argv[i][0] != '{' )
925 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
927 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
928 strncat(line, buf, 128*1024 - strlen(line) - 1 );
931 line[strlen(line)-1] = NULLCHAR;
935 //--------------------------------------------------------------------------------------------
940 // delete surfaces after size becomes invalid, so they will be recreated
941 if(csBoardWindow) cairo_surface_destroy(csBoardWindow);
942 if(csBoardBackup) cairo_surface_destroy(csBoardBackup);
943 if(csDualBoard) cairo_surface_destroy(csDualBoard);
944 csBoardWindow = csBoardBackup = csDualBoard = NULL;
947 #define BoardSize int
949 InitDrawingSizes (BoardSize boardSize, int flags)
950 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
951 Dimension boardWidth, boardHeight, w, h;
953 static Dimension oldWidth, oldHeight;
954 static VariantClass oldVariant;
955 static int oldMono = -1, oldTwoBoards = 0;
957 if(!formWidget) return;
959 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
960 oldTwoBoards = twoBoards;
962 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
963 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
964 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
966 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
968 oldWidth = boardWidth; oldHeight = boardHeight;
973 * Inhibit shell resizing.
975 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
976 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
977 shellArgs[4].value = shellArgs[2].value = w;
978 shellArgs[5].value = shellArgs[3].value = h;
979 XtSetValues(shellWidget, &shellArgs[0], cairoAnimate ? 2 : 6);
981 XSync(xDisplay, False);
985 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
988 if(gameInfo.variant != oldVariant) { // and only if variant changed
993 for(p=0; p<=(int)WhiteKing; p++)
994 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
995 if(gameInfo.variant == VariantShogi) {
996 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
997 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
998 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
999 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1000 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1003 if(gameInfo.variant == VariantGothic) {
1004 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1007 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1008 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1009 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1012 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1013 for(p=0; p<=(int)WhiteKing; p++)
1014 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1015 if(gameInfo.variant == VariantShogi) {
1016 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1017 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1018 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1019 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1020 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1023 if(gameInfo.variant == VariantGothic) {
1024 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1027 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1028 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1029 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1034 for(i=0; i<2; i++) {
1036 for(p=0; p<=(int)WhiteKing; p++)
1037 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1038 if(gameInfo.variant == VariantShogi) {
1039 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1040 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1041 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1042 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1043 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1046 if(gameInfo.variant == VariantGothic) {
1047 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1050 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1051 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1052 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1056 for(i=0; i<2; i++) {
1058 for(p=0; p<=(int)WhiteKing; p++)
1059 pngPieceBitmaps[i][p] = pngPieceBitmaps2[i][p]; // defaults
1060 if(gameInfo.variant == VariantShogi) {
1061 pngPieceBitmaps[i][(int)WhiteCannon] = pngPieceBitmaps2[i][(int)WhiteKing+1];
1062 pngPieceBitmaps[i][(int)WhiteNightrider] = pngPieceBitmaps2[i][(int)WhiteKing+2];
1063 pngPieceBitmaps[i][(int)WhiteSilver] = pngPieceBitmaps2[i][(int)WhiteKing+3];
1064 pngPieceBitmaps[i][(int)WhiteGrasshopper] = pngPieceBitmaps2[i][(int)WhiteKing+4];
1065 pngPieceBitmaps[i][(int)WhiteQueen] = pngPieceBitmaps2[i][(int)WhiteLance];
1067 if(gameInfo.variant == VariantGothic) {
1068 pngPieceBitmaps[i][(int)WhiteMarshall] = pngPieceBitmaps2[i][(int)WhiteSilver];
1070 if(gameInfo.variant == VariantSChess) {
1071 pngPieceBitmaps[i][(int)WhiteAngel] = pngPieceBitmaps2[i][(int)WhiteFalcon];
1072 pngPieceBitmaps[i][(int)WhiteMarshall] = pngPieceBitmaps2[i][(int)WhiteAlfil];
1075 oldMono = -10; // kludge to force recreation of animation masks
1076 oldVariant = gameInfo.variant;
1079 if(appData.monoMode != oldMono || cairoAnimate)
1082 oldMono = appData.monoMode;
1086 MakeOneColor (char *name, Pixel *color)
1088 XrmValue vFrom, vTo;
1089 if (!appData.monoMode) {
1090 vFrom.addr = (caddr_t) name;
1091 vFrom.size = strlen(name);
1092 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1093 if (vTo.addr == NULL) {
1094 appData.monoMode = True;
1097 *color = *(Pixel *) vTo.addr;
1105 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1106 int forceMono = False;
1108 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1109 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1110 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1111 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1112 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1113 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1114 if (appData.lowTimeWarning)
1115 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1116 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1117 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1124 { // [HGM] taken out of main
1126 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1127 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1128 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1130 if (appData.bitmapDirectory[0] != NULLCHAR) {
1134 CreateXPMBoard(appData.liteBackTextureFile, 1);
1135 CreateXPMBoard(appData.darkBackTextureFile, 0);
1137 if (appData.pngDirectory[0] != NULLCHAR) { // for now do in parallel
1142 /* Create regular pieces */
1143 if (!useImages) CreatePieces();
1148 InitDrawingParams ()
1150 MakeColors(); CreateGCs(True);
1155 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1156 { // detervtomine what fonts to use, and create them
1160 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1161 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1162 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1163 appData.font = fontTable[MESSAGE_FONT][squareSize];
1164 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1165 appData.coordFont = fontTable[COORD_FONT][squareSize];
1168 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1169 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1170 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1171 fontSet = CreateFontSet(appData.font);
1172 clockFontSet = CreateFontSet(appData.clockFont);
1174 /* For the coordFont, use the 0th font of the fontset. */
1175 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1176 XFontStruct **font_struct_list;
1177 XFontSetExtents *fontSize;
1178 char **font_name_list;
1179 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1180 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1181 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1182 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1183 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1186 appData.font = FindFont(appData.font, fontPxlSize);
1187 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1188 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1189 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1190 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1191 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1192 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1193 // textHeight in !NLS mode!
1195 countFontID = coordFontID; // [HGM] holdings
1196 countFontStruct = coordFontStruct;
1198 xdb = XtDatabase(xDisplay);
1200 XrmPutLineResource(&xdb, "*international: True");
1201 vTo.size = sizeof(XFontSet);
1202 vTo.addr = (XtPointer) &fontSet;
1203 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1205 XrmPutStringResource(&xdb, "*font", appData.font);
1210 PrintArg (ArgType t)
1215 case ArgInt: p = " N"; break;
1216 case ArgString: p = " STR"; break;
1217 case ArgBoolean: p = " TF"; break;
1218 case ArgSettingsFilename:
1219 case ArgFilename: p = " FILE"; break;
1220 case ArgX: p = " Nx"; break;
1221 case ArgY: p = " Ny"; break;
1222 case ArgAttribs: p = " TEXTCOL"; break;
1223 case ArgColor: p = " COL"; break;
1224 case ArgFont: p = " FONT"; break;
1225 case ArgBoardSize: p = " SIZE"; break;
1226 case ArgFloat: p = " FLOAT"; break;
1231 case ArgCommSettings:
1242 ArgDescriptor *q, *p = argDescriptors+5;
1243 printf("\nXBoard accepts the following options:\n"
1244 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1245 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1246 " SIZE = board-size spec(s)\n"
1247 " Within parentheses are short forms, or options to set to true or false.\n"
1248 " Persistent options (saved in the settings file) are marked with *)\n\n");
1250 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1251 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1252 if(p->save) strcat(buf+len, "*");
1253 for(q=p+1; q->argLoc == p->argLoc; q++) {
1254 if(q->argName[0] == '-') continue;
1255 strcat(buf+len, q == p+1 ? " (" : " ");
1256 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1258 if(q != p+1) strcat(buf+len, ")");
1260 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1263 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1267 main (int argc, char **argv)
1269 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1270 XSetWindowAttributes window_attributes;
1272 Dimension boardWidth, boardHeight, w, h;
1274 int forceMono = False;
1276 srandom(time(0)); // [HGM] book: make random truly random
1278 setbuf(stdout, NULL);
1279 setbuf(stderr, NULL);
1282 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1283 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1287 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1292 programName = strrchr(argv[0], '/');
1293 if (programName == NULL)
1294 programName = argv[0];
1299 XtSetLanguageProc(NULL, NULL, NULL);
1300 if (appData.debugMode) {
1301 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1304 bindtextdomain(PACKAGE, LOCALEDIR);
1305 textdomain(PACKAGE);
1308 appData.boardSize = "";
1309 InitAppData(ConvertToLine(argc, argv));
1311 if (p == NULL) p = "/tmp";
1312 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1313 gameCopyFilename = (char*) malloc(i);
1314 gamePasteFilename = (char*) malloc(i);
1315 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1316 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1318 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1319 static char buf[MSG_SIZ];
1320 EscapeExpand(buf, appData.firstInitString);
1321 appData.firstInitString = strdup(buf);
1322 EscapeExpand(buf, appData.secondInitString);
1323 appData.secondInitString = strdup(buf);
1324 EscapeExpand(buf, appData.firstComputerString);
1325 appData.firstComputerString = strdup(buf);
1326 EscapeExpand(buf, appData.secondComputerString);
1327 appData.secondComputerString = strdup(buf);
1330 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1333 if (chdir(chessDir) != 0) {
1334 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1340 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1341 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1342 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1343 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1346 setbuf(debugFP, NULL);
1349 /* [HGM,HR] make sure board size is acceptable */
1350 if(appData.NrFiles > BOARD_FILES ||
1351 appData.NrRanks > BOARD_RANKS )
1352 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1355 /* This feature does not work; animation needs a rewrite */
1356 appData.highlightDragging = FALSE;
1360 gameInfo.variant = StringToVariant(appData.variant);
1361 InitPosition(FALSE);
1364 XtAppInitialize(&appContext, "XBoard", shellOptions,
1365 XtNumber(shellOptions),
1366 &argc, argv, xboardResources, NULL, 0);
1368 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1369 clientResources, XtNumber(clientResources),
1372 xDisplay = XtDisplay(shellWidget);
1373 xScreen = DefaultScreen(xDisplay);
1374 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1377 * determine size, based on supplied or remembered -size, or screen size
1379 if (isdigit(appData.boardSize[0])) {
1380 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1381 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1382 &fontPxlSize, &smallLayout, &tinyLayout);
1384 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1385 programName, appData.boardSize);
1389 /* Find some defaults; use the nearest known size */
1390 SizeDefaults *szd, *nearest;
1391 int distance = 99999;
1392 nearest = szd = sizeDefaults;
1393 while (szd->name != NULL) {
1394 if (abs(szd->squareSize - squareSize) < distance) {
1396 distance = abs(szd->squareSize - squareSize);
1397 if (distance == 0) break;
1401 if (i < 2) lineGap = nearest->lineGap;
1402 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1403 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1404 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1405 if (i < 6) smallLayout = nearest->smallLayout;
1406 if (i < 7) tinyLayout = nearest->tinyLayout;
1409 SizeDefaults *szd = sizeDefaults;
1410 if (*appData.boardSize == NULLCHAR) {
1411 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1412 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1415 if (szd->name == NULL) szd--;
1416 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1418 while (szd->name != NULL &&
1419 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1420 if (szd->name == NULL) {
1421 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1422 programName, appData.boardSize);
1426 squareSize = szd->squareSize;
1427 lineGap = szd->lineGap;
1428 clockFontPxlSize = szd->clockFontPxlSize;
1429 coordFontPxlSize = szd->coordFontPxlSize;
1430 fontPxlSize = szd->fontPxlSize;
1431 smallLayout = szd->smallLayout;
1432 tinyLayout = szd->tinyLayout;
1433 // [HGM] font: use defaults from settings file if available and not overruled
1436 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1437 if (strlen(appData.pixmapDirectory) > 0) {
1438 p = ExpandPathName(appData.pixmapDirectory);
1440 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1441 appData.pixmapDirectory);
1444 if (appData.debugMode) {
1445 fprintf(stderr, _("\
1446 XBoard square size (hint): %d\n\
1447 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1449 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1450 if (appData.debugMode) {
1451 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1454 defaultLineGap = lineGap;
1455 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1457 /* [HR] height treated separately (hacked) */
1458 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1459 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1462 * Determine what fonts to use.
1464 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1467 * Detect if there are not enough colors available and adapt.
1469 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1470 appData.monoMode = True;
1473 forceMono = MakeColors();
1476 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1478 appData.monoMode = True;
1481 if (appData.monoMode && appData.debugMode) {
1482 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1483 (unsigned long) XWhitePixel(xDisplay, xScreen),
1484 (unsigned long) XBlackPixel(xDisplay, xScreen));
1487 ParseIcsTextColors();
1489 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1495 layoutName = "tinyLayout";
1496 } else if (smallLayout) {
1497 layoutName = "smallLayout";
1499 layoutName = "normalLayout";
1502 optList = BoardPopUp(squareSize, lineGap, (void*)
1508 boardWidget = optList[W_BOARD].handle;
1509 menuBarWidget = optList[W_MENU].handle;
1510 dropMenu = optList[W_DROP].handle;
1511 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1512 formWidget = XtParent(boardWidget);
1513 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1514 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1515 XtGetValues(optList[W_WHITE].handle, args, 2);
1516 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1517 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1518 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1519 XtGetValues(optList[W_PAUSE].handle, args, 2);
1521 AppendEnginesToMenu(appData.recentEngineList);
1523 xBoardWindow = XtWindow(boardWidget);
1525 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1526 // not need to go into InitDrawingSizes().
1529 * Create X checkmark bitmap and initialize option menu checks.
1531 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1532 checkmark_bits, checkmark_width, checkmark_height);
1538 ReadBitmap(&wIconPixmap, "icon_white.bm",
1539 icon_white_bits, icon_white_width, icon_white_height);
1540 ReadBitmap(&bIconPixmap, "icon_black.bm",
1541 icon_black_bits, icon_black_width, icon_black_height);
1542 iconPixmap = wIconPixmap;
1544 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1545 XtSetValues(shellWidget, args, i);
1548 * Create a cursor for the board widget.
1550 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1551 XChangeWindowAttributes(xDisplay, xBoardWindow,
1552 CWCursor, &window_attributes);
1555 * Inhibit shell resizing.
1559 cairoAnimate = *appData.pngDirectory && useTexture == 3
1560 && strstr(appData.liteBackTextureFile, ".png") && strstr(appData.darkBackTextureFile, ".png");
1562 shellArgs[0].value = (XtArgVal) &w;
1563 shellArgs[1].value = (XtArgVal) &h;
1564 XtGetValues(shellWidget, shellArgs, 2);
1565 shellArgs[4].value = shellArgs[2].value = w;
1566 shellArgs[5].value = shellArgs[3].value = h;
1567 if(!cairoAnimate) XtSetValues(shellWidget, &shellArgs[2], 4);
1568 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1569 marginH = h - boardHeight;
1571 CatchDeleteWindow(shellWidget, "QuitProc");
1576 if(appData.logoSize)
1577 { // locate and read user logo
1579 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1580 ASSIGN(userLogo, buf);
1583 if (appData.animate || appData.animateDragging)
1586 XtAugmentTranslations(formWidget,
1587 XtParseTranslationTable(globalTranslations));
1589 XtAddEventHandler(formWidget, KeyPressMask, False,
1590 (XtEventHandler) MoveTypeInProc, NULL);
1591 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1592 (XtEventHandler) EventProc, NULL);
1594 /* [AS] Restore layout */
1595 if( wpMoveHistory.visible ) {
1599 if( wpEvalGraph.visible )
1604 if( wpEngineOutput.visible ) {
1605 EngineOutputPopUp();
1610 if (errorExitStatus == -1) {
1611 if (appData.icsActive) {
1612 /* We now wait until we see "login:" from the ICS before
1613 sending the logon script (problems with timestamp otherwise) */
1614 /*ICSInitScript();*/
1615 if (appData.icsInputBox) ICSInputBoxPopUp();
1619 signal(SIGWINCH, TermSizeSigHandler);
1621 signal(SIGINT, IntSigHandler);
1622 signal(SIGTERM, IntSigHandler);
1623 if (*appData.cmailGameName != NULLCHAR) {
1624 signal(SIGUSR1, CmailSigHandler);
1628 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1631 // XtSetKeyboardFocus(shellWidget, formWidget);
1632 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1634 XtAppMainLoop(appContext);
1635 if (appData.debugMode) fclose(debugFP); // [DM] debug
1640 TermSizeSigHandler (int sig)
1646 IntSigHandler (int sig)
1652 CmailSigHandler (int sig)
1657 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1659 /* Activate call-back function CmailSigHandlerCallBack() */
1660 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1662 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1666 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1669 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1671 /**** end signal code ****/
1674 #define Abs(n) ((n)<0 ? -(n) : (n))
1678 InsertPxlSize (char *pattern, int targetPxlSize)
1680 char *base_fnt_lst, strInt[12], *p, *q;
1681 int alternatives, i, len, strIntLen;
1684 * Replace the "*" (if present) in the pixel-size slot of each
1685 * alternative with the targetPxlSize.
1689 while ((p = strchr(p, ',')) != NULL) {
1693 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1694 strIntLen = strlen(strInt);
1695 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1699 while (alternatives--) {
1700 char *comma = strchr(p, ',');
1701 for (i=0; i<14; i++) {
1702 char *hyphen = strchr(p, '-');
1704 if (comma && hyphen > comma) break;
1705 len = hyphen + 1 - p;
1706 if (i == 7 && *p == '*' && len == 2) {
1708 memcpy(q, strInt, strIntLen);
1718 len = comma + 1 - p;
1725 return base_fnt_lst;
1729 CreateFontSet (char *base_fnt_lst)
1732 char **missing_list;
1736 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1737 &missing_list, &missing_count, &def_string);
1738 if (appData.debugMode) {
1740 XFontStruct **font_struct_list;
1741 char **font_name_list;
1742 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1744 fprintf(debugFP, " got list %s, locale %s\n",
1745 XBaseFontNameListOfFontSet(fntSet),
1746 XLocaleOfFontSet(fntSet));
1747 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1748 for (i = 0; i < count; i++) {
1749 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1752 for (i = 0; i < missing_count; i++) {
1753 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1756 if (fntSet == NULL) {
1757 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1762 #else // not ENABLE_NLS
1764 * Find a font that matches "pattern" that is as close as
1765 * possible to the targetPxlSize. Prefer fonts that are k
1766 * pixels smaller to fonts that are k pixels larger. The
1767 * pattern must be in the X Consortium standard format,
1768 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1769 * The return value should be freed with XtFree when no
1773 FindFont (char *pattern, int targetPxlSize)
1775 char **fonts, *p, *best, *scalable, *scalableTail;
1776 int i, j, nfonts, minerr, err, pxlSize;
1778 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1780 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1781 programName, pattern);
1788 for (i=0; i<nfonts; i++) {
1791 if (*p != '-') continue;
1793 if (*p == NULLCHAR) break;
1794 if (*p++ == '-') j++;
1796 if (j < 7) continue;
1799 scalable = fonts[i];
1802 err = pxlSize - targetPxlSize;
1803 if (Abs(err) < Abs(minerr) ||
1804 (minerr > 0 && err < 0 && -err == minerr)) {
1810 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1811 /* If the error is too big and there is a scalable font,
1812 use the scalable font. */
1813 int headlen = scalableTail - scalable;
1814 p = (char *) XtMalloc(strlen(scalable) + 10);
1815 while (isdigit(*scalableTail)) scalableTail++;
1816 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1818 p = (char *) XtMalloc(strlen(best) + 2);
1819 safeStrCpy(p, best, strlen(best)+1 );
1821 if (appData.debugMode) {
1822 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1823 pattern, targetPxlSize, p);
1825 XFreeFontNames(fonts);
1832 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1833 // must be called before all non-first callse to CreateGCs()
1834 XtReleaseGC(shellWidget, highlineGC);
1835 XtReleaseGC(shellWidget, lightSquareGC);
1836 XtReleaseGC(shellWidget, darkSquareGC);
1837 XtReleaseGC(shellWidget, lineGC);
1838 if (appData.monoMode) {
1839 if (DefaultDepth(xDisplay, xScreen) == 1) {
1840 XtReleaseGC(shellWidget, wbPieceGC);
1842 XtReleaseGC(shellWidget, bwPieceGC);
1845 XtReleaseGC(shellWidget, prelineGC);
1846 XtReleaseGC(shellWidget, wdPieceGC);
1847 XtReleaseGC(shellWidget, wlPieceGC);
1848 XtReleaseGC(shellWidget, bdPieceGC);
1849 XtReleaseGC(shellWidget, blPieceGC);
1854 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1856 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1857 | GCBackground | GCFunction | GCPlaneMask;
1858 gc_values->foreground = foreground;
1859 gc_values->background = background;
1860 return XtGetGC(shellWidget, value_mask, gc_values);
1864 CreateGCs (int redo)
1866 XGCValues gc_values;
1868 Pixel white = XWhitePixel(xDisplay, xScreen);
1869 Pixel black = XBlackPixel(xDisplay, xScreen);
1871 gc_values.plane_mask = AllPlanes;
1872 gc_values.line_width = lineGap;
1873 gc_values.line_style = LineSolid;
1874 gc_values.function = GXcopy;
1877 DeleteGCs(); // called a second time; clean up old GCs first
1878 } else { // [HGM] grid and font GCs created on first call only
1879 coordGC = CreateOneGC(&gc_values, black, white);
1880 XSetFont(xDisplay, coordGC, coordFontID);
1882 // [HGM] make font for holdings counts (white on black)
1883 countGC = CreateOneGC(&gc_values, white, black);
1884 XSetFont(xDisplay, countGC, countFontID);
1886 lineGC = CreateOneGC(&gc_values, black, black);
1888 if (appData.monoMode) {
1890 highlineGC = CreateOneGC(&gc_values, white, white);
1891 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1892 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1894 if (DefaultDepth(xDisplay, xScreen) == 1) {
1895 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1896 gc_values.function = GXcopyInverted;
1897 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1898 gc_values.function = GXcopy;
1899 if (XBlackPixel(xDisplay, xScreen) == 1) {
1900 bwPieceGC = darkSquareGC;
1901 wbPieceGC = copyInvertedGC;
1903 bwPieceGC = copyInvertedGC;
1904 wbPieceGC = lightSquareGC;
1909 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1910 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1911 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1912 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1913 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1914 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1915 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1916 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1921 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1929 fp = fopen(filename, "rb");
1931 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1938 for (y=0; y<h; ++y) {
1939 for (x=0; x<h; ++x) {
1944 XPutPixel(xim, x, y, blackPieceColor);
1946 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1949 XPutPixel(xim, x, y, darkSquareColor);
1951 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1954 XPutPixel(xim, x, y, whitePieceColor);
1956 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1959 XPutPixel(xim, x, y, lightSquareColor);
1961 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1969 /* create Pixmap of piece */
1970 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1972 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1975 /* create Pixmap of clipmask
1976 Note: We assume the white/black pieces have the same
1977 outline, so we make only 6 masks. This is okay
1978 since the XPM clipmask routines do the same. */
1980 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1982 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1985 /* now create the 1-bit version */
1986 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1989 values.foreground = 1;
1990 values.background = 0;
1992 /* Don't use XtGetGC, not read only */
1993 maskGC = XCreateGC(xDisplay, *mask,
1994 GCForeground | GCBackground, &values);
1995 XCopyPlane(xDisplay, temp, *mask, maskGC,
1996 0, 0, squareSize, squareSize, 0, 0, 1);
1997 XFreePixmap(xDisplay, temp);
2002 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2010 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2015 /* The XSynchronize calls were copied from CreatePieces.
2016 Not sure if needed, but can't hurt */
2017 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2020 /* temp needed by loadXIM() */
2021 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2022 0, 0, ss, ss, AllPlanes, XYPixmap);
2024 if (strlen(appData.pixmapDirectory) == 0) {
2028 if (appData.monoMode) {
2029 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2033 fprintf(stderr, _("\nLoading XIMs...\n"));
2035 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2036 fprintf(stderr, "%d", piece+1);
2037 for (kind=0; kind<4; kind++) {
2038 fprintf(stderr, ".");
2039 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2040 ExpandPathName(appData.pixmapDirectory),
2041 piece <= (int) WhiteKing ? "" : "w",
2042 pieceBitmapNames[piece],
2044 ximPieceBitmap[kind][piece] =
2045 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2046 0, 0, ss, ss, AllPlanes, XYPixmap);
2047 if (appData.debugMode)
2048 fprintf(stderr, _("(File:%s:) "), buf);
2049 loadXIM(ximPieceBitmap[kind][piece],
2051 &(xpmPieceBitmap2[kind][piece]),
2052 &(ximMaskPm2[piece]));
2053 if(piece <= (int)WhiteKing)
2054 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2056 fprintf(stderr," ");
2058 /* Load light and dark squares */
2059 /* If the LSQ and DSQ pieces don't exist, we will
2060 draw them with solid squares. */
2061 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2062 if (access(buf, 0) != 0) {
2066 fprintf(stderr, _("light square "));
2068 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2069 0, 0, ss, ss, AllPlanes, XYPixmap);
2070 if (appData.debugMode)
2071 fprintf(stderr, _("(File:%s:) "), buf);
2073 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2074 fprintf(stderr, _("dark square "));
2075 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2076 ExpandPathName(appData.pixmapDirectory), ss);
2077 if (appData.debugMode)
2078 fprintf(stderr, _("(File:%s:) "), buf);
2080 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2081 0, 0, ss, ss, AllPlanes, XYPixmap);
2082 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2083 xpmJailSquare = xpmLightSquare;
2085 fprintf(stderr, _("Done.\n"));
2087 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2090 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2094 CreateXPMBoard (char *s, int kind)
2098 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2099 if(strstr(s, ".png")) {
2100 cairo_surface_t *img = cairo_image_surface_create_from_png (s);
2102 useTexture |= kind + 1; pngBoardBitmap[kind] = img;
2103 textureW[kind] = cairo_image_surface_get_width (img);
2104 textureH[kind] = cairo_image_surface_get_height (img);
2107 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2108 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2114 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2115 // thisroutine has to be called t free the old piece pixmaps
2117 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2118 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2120 XFreePixmap(xDisplay, xpmLightSquare);
2121 XFreePixmap(xDisplay, xpmDarkSquare);
2130 u_int ss = squareSize;
2132 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2133 XpmColorSymbol symbols[4];
2134 static int redo = False;
2136 if(redo) FreeXPMPieces(); else redo = 1;
2138 /* The XSynchronize calls were copied from CreatePieces.
2139 Not sure if needed, but can't hurt */
2140 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2142 /* Setup translations so piece colors match square colors */
2143 symbols[0].name = "light_piece";
2144 symbols[0].value = appData.whitePieceColor;
2145 symbols[1].name = "dark_piece";
2146 symbols[1].value = appData.blackPieceColor;
2147 symbols[2].name = "light_square";
2148 symbols[2].value = appData.lightSquareColor;
2149 symbols[3].name = "dark_square";
2150 symbols[3].value = appData.darkSquareColor;
2152 attr.valuemask = XpmColorSymbols;
2153 attr.colorsymbols = symbols;
2154 attr.numsymbols = 4;
2156 if (appData.monoMode) {
2157 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2161 if (strlen(appData.pixmapDirectory) == 0) {
2162 XpmPieces* pieces = builtInXpms;
2165 while (pieces->size != squareSize && pieces->size) pieces++;
2166 if (!pieces->size) {
2167 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2170 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2171 for (kind=0; kind<4; kind++) {
2173 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2174 pieces->xpm[piece][kind],
2175 &(xpmPieceBitmap2[kind][piece]),
2176 NULL, &attr)) != 0) {
2177 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2181 if(piece <= (int) WhiteKing)
2182 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2186 xpmJailSquare = xpmLightSquare;
2190 fprintf(stderr, _("\nLoading XPMs...\n"));
2193 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2194 fprintf(stderr, "%d ", piece+1);
2195 for (kind=0; kind<4; kind++) {
2196 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2197 ExpandPathName(appData.pixmapDirectory),
2198 piece > (int) WhiteKing ? "w" : "",
2199 pieceBitmapNames[piece],
2201 if (appData.debugMode) {
2202 fprintf(stderr, _("(File:%s:) "), buf);
2204 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2205 &(xpmPieceBitmap2[kind][piece]),
2206 NULL, &attr)) != 0) {
2207 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2208 // [HGM] missing: read of unorthodox piece failed; substitute King.
2209 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2210 ExpandPathName(appData.pixmapDirectory),
2212 if (appData.debugMode) {
2213 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2215 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2216 &(xpmPieceBitmap2[kind][piece]),
2220 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2225 if(piece <= (int) WhiteKing)
2226 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2229 /* Load light and dark squares */
2230 /* If the LSQ and DSQ pieces don't exist, we will
2231 draw them with solid squares. */
2232 fprintf(stderr, _("light square "));
2233 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2234 if (access(buf, 0) != 0) {
2238 if (appData.debugMode)
2239 fprintf(stderr, _("(File:%s:) "), buf);
2241 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2242 &xpmLightSquare, NULL, &attr)) != 0) {
2243 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2246 fprintf(stderr, _("dark square "));
2247 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2248 ExpandPathName(appData.pixmapDirectory), ss);
2249 if (appData.debugMode) {
2250 fprintf(stderr, _("(File:%s:) "), buf);
2252 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2253 &xpmDarkSquare, NULL, &attr)) != 0) {
2254 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2258 xpmJailSquare = xpmLightSquare;
2259 fprintf(stderr, _("Done.\n"));
2261 oldVariant = -1; // kludge to force re-makig of animation masks
2262 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2265 #endif /* HAVE_LIBXPM */
2267 char *pngPieceNames[] = // must be in same order as internal piece encoding
2268 { "Pawn", "Knight", "Bishop", "Rook", "Queen", "Advisor", "Elephant", "Archbishop", "Marshall", "Gold", "Commoner",
2269 "Canon", "Nightrider", "CrownedBishop", "CrownedRook", "Princess", "Chancellor", "Hawk", "Lance", "Cobra", "Unicorn", "King",
2270 "GoldKnight", "GoldLance", "GoldPawn", "GoldSilver", NULL
2274 ScaleOnePiece (char *name, int color, int piece)
2278 cairo_surface_t *img, *cs;
2280 static cairo_surface_t *pngPieceImages[2][(int)BlackPawn+4]; // png 256 x 256 images
2282 if((img = pngPieceImages[color][piece]) == NULL) { // if PNG file for this piece was not yet read, read it now and store it
2283 snprintf(buf, MSG_SIZ, "%s/%s%s.png", appData.pngDirectory, color ? "Black" : "White", pngPieceNames[piece]);
2284 pngPieceImages[color][piece] = img = cairo_image_surface_create_from_png (buf);
2285 w = cairo_image_surface_get_width (img);
2286 h = cairo_image_surface_get_height (img);
2287 if(w != 64 || h != 64) { printf("Bad png size %dx%d in %s\n", w, h, buf); exit(1); }
2289 // create new bitmap to hold scaled piece image (and remove any old)
2290 if(pngPieceBitmaps2[color][piece]) cairo_surface_destroy (pngPieceBitmaps2[color][piece]);
2291 pngPieceBitmaps2[color][piece] = cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
2292 if(piece <= WhiteKing) pngPieceBitmaps[color][piece] = cs;
2293 // scaled copying of the raw png image
2294 cr = cairo_create(cs);
2295 cairo_scale(cr, squareSize/64., squareSize/64.);
2296 cairo_set_source_surface (cr, img, 0, 0);
2306 for(p=0; pngPieceNames[p]; p++) {
2307 ScaleOnePiece(pngPieceNames[p], 0, p);
2308 ScaleOnePiece(pngPieceNames[p], 1, p);
2313 /* No built-in bitmaps */
2318 u_int ss = squareSize;
2320 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2323 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2324 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2325 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2326 pieceBitmapNames[piece],
2327 ss, kind == SOLID ? 's' : 'o');
2328 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2329 if(piece <= (int)WhiteKing)
2330 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2334 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2338 /* With built-in bitmaps */
2342 BuiltInBits* bib = builtInBits;
2345 u_int ss = squareSize;
2347 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2350 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2352 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2353 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2354 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2355 pieceBitmapNames[piece],
2356 ss, kind == SOLID ? 's' : 'o');
2357 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2358 bib->bits[kind][piece], ss, ss);
2359 if(piece <= (int)WhiteKing)
2360 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2364 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2370 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2375 char msg[MSG_SIZ], fullname[MSG_SIZ];
2377 if (*appData.bitmapDirectory != NULLCHAR) {
2378 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2379 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2380 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2381 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2382 &w, &h, pm, &x_hot, &y_hot);
2383 fprintf(stderr, "load %s\n", name);
2384 if (errcode != BitmapSuccess) {
2386 case BitmapOpenFailed:
2387 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2389 case BitmapFileInvalid:
2390 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2392 case BitmapNoMemory:
2393 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2397 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2401 fprintf(stderr, _("%s: %s...using built-in\n"),
2403 } else if (w != wreq || h != hreq) {
2405 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2406 programName, fullname, w, h, wreq, hreq);
2412 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2422 if (lineGap == 0) return;
2424 /* [HR] Split this into 2 loops for non-square boards. */
2426 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2427 gridSegments[i].x1 = 0;
2428 gridSegments[i].x2 =
2429 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2430 gridSegments[i].y1 = gridSegments[i].y2
2431 = lineGap / 2 + (i * (squareSize + lineGap));
2434 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2435 gridSegments[j + i].y1 = 0;
2436 gridSegments[j + i].y2 =
2437 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2438 gridSegments[j + i].x1 = gridSegments[j + i].x2
2439 = lineGap / 2 + (j * (squareSize + lineGap));
2444 MarkMenuItem (char *menuRef, int state)
2446 MenuItem *item = MenuNameToItem(menuRef);
2450 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2451 XtSetValues(item->handle, args, 1);
2456 EnableNamedMenuItem (char *menuRef, int state)
2458 MenuItem *item = MenuNameToItem(menuRef);
2460 if(item) XtSetSensitive(item->handle, state);
2464 EnableButtonBar (int state)
2466 XtSetSensitive(optList[W_BUTTON].handle, state);
2471 SetMenuEnables (Enables *enab)
2473 while (enab->name != NULL) {
2474 EnableNamedMenuItem(enab->name, enab->value);
2480 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2481 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2483 if(*nprms == 0) return;
2484 item = MenuNameToItem(prms[0]);
2485 if(item) ((MenuProc *) item->proc) ();
2489 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2491 RecentEngineEvent((int) (intptr_t) addr);
2495 AppendMenuItem (char *msg, int n)
2497 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2509 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2510 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2511 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2512 dmEnables[i].piece);
2513 XtSetSensitive(entry, p != NULL || !appData.testLegality
2514 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2515 && !appData.icsActive));
2517 while (p && *p++ == dmEnables[i].piece) count++;
2518 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2520 XtSetArg(args[j], XtNlabel, label); j++;
2521 XtSetValues(entry, args, j);
2527 do_flash_delay (unsigned long msec)
2533 DoDrawBorder (cairo_surface_t *cs, int x, int y, int type)
2540 case 0: col = "#000000"; break;
2541 case 1: col = appData.highlightSquareColor; break;
2542 case 2: col = appData.premoveHighlightColor; break;
2544 cr = cairo_create(cs);
2545 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
2546 cairo_rectangle(cr, x, y, squareSize+lineGap, squareSize+lineGap);
2547 SetPen(cr, lineGap, col, 0);
2552 DrawBorder (int x, int y, int type)
2554 DoDrawBorder(csBoardWindow, x, y, type);
2555 DoDrawBorder(csBoardBackup, x, y, type);
2559 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2561 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2562 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2564 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2565 if(textureW[kind] < W*squareSize)
2566 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2568 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2569 if(textureH[kind] < H*squareSize)
2570 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2572 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2577 DrawLogo (void *handle, void *logo)
2579 cairo_surface_t *img, *cs;
2583 if(!logo || !handle) return;
2584 cs = cairo_xlib_surface_create(xDisplay, XtWindow(handle), DefaultVisual(xDisplay, 0), appData.logoSize, appData.logoSize/2);
2585 img = cairo_image_surface_create_from_png (logo);
2586 w = cairo_image_surface_get_width (img);
2587 h = cairo_image_surface_get_height (img);
2588 cr = cairo_create(cs);
2589 cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
2590 cairo_set_source_surface (cr, img, 0, 0);
2593 cairo_surface_destroy (img);
2594 cairo_surface_destroy (cs);
2598 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2599 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2601 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2602 if(pngBoardBitmap[color]) {
2604 if(!fac && !cairoAnimate) return;
2606 cr = cairo_create (fac ? csBoardWindow : (cairo_surface_t *) dest);
2607 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2608 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2609 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2613 cr = cairo_create (csBoardBackup);
2614 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2615 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2616 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2621 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2622 squareSize, squareSize, x*fac, y*fac);
2625 cairo_t *cr = cairo_create (csBoardWindow);
2628 case 0: col = appData.darkSquareColor; break;
2629 case 1: col = appData.lightSquareColor; break;
2630 case 2: col = "#000000"; break;
2632 SetPen(cr, 2.0, col, 0);
2633 cairo_rectangle (cr, x, y, squareSize, squareSize);
2636 cr = cairo_create (csBoardBackup);
2637 SetPen(cr, 2.0, col, 0);
2638 cairo_rectangle (cr, x, y, squareSize, squareSize);
2642 if (useImages && useImageSqs) {
2646 pm = xpmLightSquare;
2651 case 2: /* neutral */
2653 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2656 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2657 squareSize, squareSize, x*fac, y*fac);
2667 case 2: /* neutral */
2672 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2677 I split out the routines to draw a piece so that I could
2678 make a generic flash routine.
2681 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2683 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2684 switch (square_color) {
2686 case 2: /* neutral */
2688 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2689 ? *pieceToOutline(piece)
2690 : *pieceToSolid(piece),
2691 dest, bwPieceGC, 0, 0,
2692 squareSize, squareSize, x, y);
2695 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2696 ? *pieceToSolid(piece)
2697 : *pieceToOutline(piece),
2698 dest, wbPieceGC, 0, 0,
2699 squareSize, squareSize, x, y);
2705 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2707 switch (square_color) {
2709 case 2: /* neutral */
2711 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2712 ? *pieceToOutline(piece)
2713 : *pieceToSolid(piece),
2714 dest, bwPieceGC, 0, 0,
2715 squareSize, squareSize, x, y, 1);
2718 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2719 ? *pieceToSolid(piece)
2720 : *pieceToOutline(piece),
2721 dest, wbPieceGC, 0, 0,
2722 squareSize, squareSize, x, y, 1);
2728 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2730 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2731 switch (square_color) {
2733 XCopyPlane(xDisplay, *pieceToSolid(piece),
2734 dest, (int) piece < (int) BlackPawn
2735 ? wlPieceGC : blPieceGC, 0, 0,
2736 squareSize, squareSize, x, y, 1);
2739 XCopyPlane(xDisplay, *pieceToSolid(piece),
2740 dest, (int) piece < (int) BlackPawn
2741 ? wdPieceGC : bdPieceGC, 0, 0,
2742 squareSize, squareSize, x, y, 1);
2744 case 2: /* neutral */
2746 break; // should never contain pieces
2751 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2753 int kind, p = piece;
2755 switch (square_color) {
2757 case 2: /* neutral */
2759 if ((int)piece < (int) BlackPawn) {
2767 if ((int)piece < (int) BlackPawn) {
2775 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2776 if(useTexture & square_color+1) {
2777 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2778 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2779 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2780 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2781 XSetClipMask(xDisplay, wlPieceGC, None);
2782 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2784 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2785 dest, wlPieceGC, 0, 0,
2786 squareSize, squareSize, x, y);
2790 pngDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2792 int kind, p = piece;
2795 if ((int)piece < (int) BlackPawn) {
2801 if(appData.upsideDown && flipView) { p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2802 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2804 cr = cairo_create (csBoardWindow);
2805 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2808 cr = cairo_create (csBoardBackup);
2809 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2814 typedef void (*DrawFunc)();
2819 if (appData.monoMode) {
2820 if (DefaultDepth(xDisplay, xScreen) == 1) {
2821 return monoDrawPiece_1bit;
2823 return monoDrawPiece;
2825 } else if(appData.pngDirectory[0]) {
2826 return pngDrawPiece;
2829 return colorDrawPieceImage;
2831 return colorDrawPiece;
2836 DoDrawDot (int marker, int x, int y, int r, cairo_surface_t *cs)
2840 cr = cairo_create(cs);
2841 cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
2842 if(appData.monoMode) {
2843 SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
2844 cairo_stroke_preserve(cr);
2845 SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
2847 SetPen(cr, 2, marker == 2 ? "#FF0000" : "#FFFF00", 0);
2856 DrawDot (int marker, int x, int y, int r)
2858 DoDrawDot(marker, x, y, r, csBoardWindow);
2859 DoDrawDot(marker, x, y, r, csBoardBackup);
2863 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2864 { // basic front-end board-draw function: takes care of everything that can be in square:
2865 // piece, background, coordinate/count, marker dot
2866 int direction, font_ascent, font_descent;
2867 XCharStruct overall;
2870 if (piece == EmptySquare) {
2871 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2873 drawfunc = ChooseDrawFunc();
2874 drawfunc(piece, square_color, x, y, xBoardWindow);
2877 if(align) { // square carries inscription (coord or piece count)
2879 GC hGC = align < 3 ? coordGC : countGC;
2880 // first calculate where it goes
2881 XTextExtents(countFontStruct, string, 1, &direction,
2882 &font_ascent, &font_descent, &overall);
2884 xx += squareSize - overall.width - 2;
2885 yy += squareSize - font_descent - 1;
2886 } else if (align == 2) {
2887 xx += 2, yy += font_ascent + 1;
2888 } else if (align == 3) {
2889 xx += squareSize - overall.width - 2;
2890 yy += font_ascent + 1;
2891 } else if (align == 4) {
2892 xx += 2, yy += font_ascent + 1;
2895 if (appData.monoMode) {
2896 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2898 if(*appData.pngDirectory) {
2899 cairo_t *cr = cairo_create (csBoardWindow);
2900 cairo_select_font_face (cr, "Sans",
2901 CAIRO_FONT_SLANT_NORMAL,
2902 CAIRO_FONT_WEIGHT_BOLD);
2904 cairo_set_font_size (cr, squareSize/4);
2906 cairo_move_to (cr, xx-1, yy);
2907 if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2908 else cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
2909 cairo_show_text (cr, string);
2911 cr = cairo_create (csBoardBackup);
2912 cairo_select_font_face (cr, "Sans",
2913 CAIRO_FONT_SLANT_NORMAL,
2914 CAIRO_FONT_WEIGHT_BOLD);
2916 cairo_set_font_size (cr, squareSize/4);
2918 cairo_move_to (cr, xx-1, yy);
2919 if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2920 else cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
2921 cairo_show_text (cr, string);
2924 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2928 if(marker) { // print fat marker dot, if requested
2929 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2934 FlashDelay (int flash_delay)
2936 XSync(xDisplay, False);
2937 if(flash_delay) do_flash_delay(flash_delay);
2941 Fraction (int x, int start, int stop)
2943 double f = ((double) x - start)/(stop - start);
2944 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2948 static WindowPlacement wpNew;
2951 CoDrag (Widget sh, WindowPlacement *wp)
2954 int j=0, touch=0, fudge = 2;
2955 GetActualPlacement(sh, wp);
2956 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2957 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2958 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2959 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2960 if(!touch ) return; // only windows that touch co-move
2961 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2962 int heightInc = wpNew.height - wpMain.height;
2963 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2964 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2965 wp->y += fracTop * heightInc;
2966 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2967 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2968 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2969 int widthInc = wpNew.width - wpMain.width;
2970 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2971 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2972 wp->y += fracLeft * widthInc;
2973 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2974 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2976 wp->x += wpNew.x - wpMain.x;
2977 wp->y += wpNew.y - wpMain.y;
2978 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2979 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2980 XtSetArg(args[j], XtNx, wp->x); j++;
2981 XtSetArg(args[j], XtNy, wp->y); j++;
2982 XtSetValues(sh, args, j);
2986 ReSize (WindowPlacement *wp)
2989 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
2990 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
2991 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
2992 if(sqy < sqx) sqx = sqy;
2993 if(sqx != squareSize) {
2994 squareSize = sqx; // adopt new square size
2996 CreatePNGPieces(); // make newly scaled pieces
2997 InitDrawingSizes(0, 0); // creates grid etc.
3001 static XtIntervalId delayedDragID = 0;
3010 GetActualPlacement(shellWidget, &wpNew);
3011 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3012 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
3013 busy = 0; return; // false alarm
3016 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
3017 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3018 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
3019 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
3021 DrawPosition(True, NULL);
3022 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3030 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3032 XtAppAddTimeOut(appContext, 100, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3036 EventProc (Widget widget, caddr_t unused, XEvent *event)
3038 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
3039 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3042 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
3045 Color (char *col, int n)
3048 sscanf(col, "#%x", &c);
3054 SetPen (cairo_t *cr, float w, char *col, int dash)
3056 static const double dotted[] = {4.0, 4.0};
3057 static int len = sizeof(dotted) / sizeof(dotted[0]);
3058 cairo_set_line_width (cr, w);
3059 cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
3060 if(dash) cairo_set_dash (cr, dotted, len, 0.0);
3063 void DrawSeekAxis( int x, int y, int xTo, int yTo )
3068 cr = cairo_create (csBoardWindow);
3070 cairo_move_to (cr, x, y);
3071 cairo_line_to(cr, xTo, yTo );
3073 SetPen(cr, 2, "#000000", 0);
3080 void DrawSeekBackground( int left, int top, int right, int bottom )
3082 cairo_t *cr = cairo_create (csBoardWindow);
3084 cairo_rectangle (cr, left, top, right-left, bottom-top);
3086 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
3093 void DrawSeekText(char *buf, int x, int y)
3095 cairo_t *cr = cairo_create (csBoardWindow);
3097 cairo_select_font_face (cr, "Sans",
3098 CAIRO_FONT_SLANT_NORMAL,
3099 CAIRO_FONT_WEIGHT_NORMAL);
3101 cairo_set_font_size (cr, 12.0);
3103 cairo_move_to (cr, x, y+4);
3104 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
3105 cairo_show_text( cr, buf);
3111 void DrawSeekDot(int x, int y, int colorNr)
3113 cairo_t *cr = cairo_create (csBoardWindow);
3114 int square = colorNr & 0x80;
3118 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3120 cairo_arc(cr, x, y, squareSize/8, 0.0, 2*M_PI);
3122 SetPen(cr, 2, "#000000", 0);
3123 cairo_stroke_preserve(cr);
3125 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
3126 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
3127 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
3138 int boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3139 int boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3140 if(!csBoardWindow) {
3141 csBoardWindow = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), boardWidth, boardHeight);
3142 csBoardBackup = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, boardWidth, boardHeight);
3152 DoDrawGrid(cairo_surface_t *cs)
3154 /* draws a grid starting around Nx, Ny squares starting at x,y */
3160 cr = cairo_create (cs);
3162 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
3163 SetPen(cr, lineGap, "#000000", 0);
3166 for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
3168 cairo_move_to (cr, gridSegments[i].x1, gridSegments[i].y1);
3169 cairo_line_to (cr, gridSegments[i].x2, gridSegments[i].y2);
3182 DoDrawGrid(csBoardWindow);
3183 DoDrawGrid(csBoardBackup);
3187 * event handler for redrawing the board
3190 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3192 DrawPosition(True, NULL);
3197 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3198 { // [HGM] pv: walk PV
3199 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3202 static int savedIndex; /* gross that this is global */
3205 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3208 XawTextPosition index, dummy;
3211 XawTextGetSelectionPos(w, &index, &dummy);
3212 XtSetArg(arg, XtNstring, &val);
3213 XtGetValues(w, &arg, 1);
3214 ReplaceComment(savedIndex, val);
3215 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3216 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3220 EditCommentPopUp (int index, char *title, char *text)
3223 if (text == NULL) text = "";
3224 NewCommentPopup(title, text, index);
3228 CommentPopUp (char *title, char *text)
3230 savedIndex = currentMove; // [HGM] vari
3231 NewCommentPopup(title, text, currentMove);
3237 PopDown(CommentDlg);
3241 /* Disable all user input other than deleting the window */
3242 static int frozen = 0;
3248 /* Grab by a widget that doesn't accept input */
3249 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
3253 /* Undo a FreezeUI */
3257 if (!frozen) return;
3258 XtRemoveGrab(optList[W_MESSG].handle);
3266 static int oldPausing = FALSE;
3267 static GameMode oldmode = (GameMode) -1;
3270 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3272 if (pausing != oldPausing) {
3273 oldPausing = pausing;
3274 MarkMenuItem("Mode.Pause", pausing);
3276 if (appData.showButtonBar) {
3277 /* Always toggle, don't set. Previous code messes up when
3278 invoked while the button is pressed, as releasing it
3279 toggles the state again. */
3282 XtSetArg(args[0], XtNbackground, &oldbg);
3283 XtSetArg(args[1], XtNforeground, &oldfg);
3284 XtGetValues(optList[W_PAUSE].handle,
3286 XtSetArg(args[0], XtNbackground, oldfg);
3287 XtSetArg(args[1], XtNforeground, oldbg);
3289 XtSetValues(optList[W_PAUSE].handle, args, 2);
3293 wname = ModeToWidgetName(oldmode);
3294 if (wname != NULL) {
3295 MarkMenuItem(wname, False);
3297 wname = ModeToWidgetName(gameMode);
3298 if (wname != NULL) {
3299 MarkMenuItem(wname, True);
3302 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
3304 /* Maybe all the enables should be handled here, not just this one */
3305 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
3307 DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
3312 * Button/menu procedures
3315 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3316 char *selected_fen_position=NULL;
3319 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3320 Atom *type_return, XtPointer *value_return,
3321 unsigned long *length_return, int *format_return)
3323 char *selection_tmp;
3325 // if (!selected_fen_position) return False; /* should never happen */
3326 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3327 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3328 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3331 if (f == NULL) return False;
3335 selection_tmp = XtMalloc(len + 1);
3336 count = fread(selection_tmp, 1, len, f);
3339 XtFree(selection_tmp);
3342 selection_tmp[len] = NULLCHAR;
3344 /* note: since no XtSelectionDoneProc was registered, Xt will
3345 * automatically call XtFree on the value returned. So have to
3346 * make a copy of it allocated with XtMalloc */
3347 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3348 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3351 *value_return=selection_tmp;
3352 *length_return=strlen(selection_tmp);
3353 *type_return=*target;
3354 *format_return = 8; /* bits per byte */
3356 } else if (*target == XA_TARGETS(xDisplay)) {
3357 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3358 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3359 targets_tmp[1] = XA_STRING;
3360 *value_return = targets_tmp;
3361 *type_return = XA_ATOM;
3364 // This code leads to a read of value_return out of bounds on 64-bit systems.
3365 // Other code which I have seen always sets *format_return to 32 independent of
3366 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3367 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3368 *format_return = 8 * sizeof(Atom);
3369 if (*format_return > 32) {
3370 *length_return *= *format_return / 32;
3371 *format_return = 32;
3374 *format_return = 32;
3382 /* note: when called from menu all parameters are NULL, so no clue what the
3383 * Widget which was clicked on was, or what the click event was
3386 CopySomething (char *src)
3388 selected_fen_position = src;
3390 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3391 * have a notion of a position that is selected but not copied.
3392 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3394 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3396 SendPositionSelection,
3397 NULL/* lose_ownership_proc */ ,
3398 NULL/* transfer_done_proc */);
3399 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3401 SendPositionSelection,
3402 NULL/* lose_ownership_proc */ ,
3403 NULL/* transfer_done_proc */);
3406 /* function called when the data to Paste is ready */
3408 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3409 Atom *type, XtPointer value, unsigned long *len, int *format)
3412 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3413 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3414 EditPositionPasteFEN(fenstr);
3418 /* called when Paste Position button is pressed,
3419 * all parameters will be NULL */
3421 PastePositionProc ()
3423 XtGetSelectionValue(menuBarWidget,
3424 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3425 /* (XtSelectionCallbackProc) */ PastePositionCB,
3426 NULL, /* client_data passed to PastePositionCB */
3428 /* better to use the time field from the event that triggered the
3429 * call to this function, but that isn't trivial to get
3436 /* note: when called from menu all parameters are NULL, so no clue what the
3437 * Widget which was clicked on was, or what the click event was
3439 /* function called when the data to Paste is ready */
3441 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3442 Atom *type, XtPointer value, unsigned long *len, int *format)
3445 if (value == NULL || *len == 0) {
3446 return; /* nothing had been selected to copy */
3448 f = fopen(gamePasteFilename, "w");
3450 DisplayError(_("Can't open temp file"), errno);
3453 fwrite(value, 1, *len, f);
3456 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3459 /* called when Paste Game button is pressed,
3460 * all parameters will be NULL */
3464 XtGetSelectionValue(menuBarWidget,
3465 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3466 /* (XtSelectionCallbackProc) */ PasteGameCB,
3467 NULL, /* client_data passed to PasteGameCB */
3469 /* better to use the time field from the event that triggered the
3470 * call to this function, but that isn't trivial to get
3479 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3486 { // bassic primitive for determining if modifier keys are pressed
3487 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3490 XQueryKeymap(xDisplay,keys);
3491 for(i=0; i<6; i++) {
3493 j = XKeysymToKeycode(xDisplay, codes[i]);
3494 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3500 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3504 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3505 if ( n == 1 && *buf >= 32 // printable
3506 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3507 ) BoxAutoPopUp (buf);
3511 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3512 { // [HGM] input: let up-arrow recall previous line from history
3517 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3518 { // [HGM] input: let down-arrow recall next line from history
3523 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3529 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3531 if (!TempBackwardActive) {
3532 TempBackwardActive = True;
3538 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3540 /* Check to see if triggered by a key release event for a repeating key.
3541 * If so the next queued event will be a key press of the same key at the same time */
3542 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3544 XPeekEvent(xDisplay, &next);
3545 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3546 next.xkey.keycode == event->xkey.keycode)
3550 TempBackwardActive = False;
3554 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3555 { // called as key binding
3558 if (nprms && *nprms > 0)
3562 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3568 { // called from menu
3569 ManInner(NULL, NULL, NULL, NULL);
3573 SetWindowTitle (char *text, char *title, char *icon)
3577 if (appData.titleInWindow) {
3579 XtSetArg(args[i], XtNlabel, text); i++;
3580 XtSetValues(titleWidget, args, i);
3583 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3584 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3585 XtSetValues(shellWidget, args, i);
3586 XSync(xDisplay, False);
3591 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3597 DisplayIcsInteractionTitle (String message)
3599 if (oldICSInteractionTitle == NULL) {
3600 /* Magic to find the old window title, adapted from vim */
3601 char *wina = getenv("WINDOWID");
3603 Window win = (Window) atoi(wina);
3604 Window root, parent, *children;
3605 unsigned int nchildren;
3606 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3608 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3609 if (!XQueryTree(xDisplay, win, &root, &parent,
3610 &children, &nchildren)) break;
3611 if (children) XFree((void *)children);
3612 if (parent == root || parent == 0) break;
3615 XSetErrorHandler(oldHandler);
3617 if (oldICSInteractionTitle == NULL) {
3618 oldICSInteractionTitle = "xterm";
3621 printf("\033]0;%s\007", message);
3626 XtIntervalId delayedEventTimerXID = 0;
3627 DelayedEventCallback delayedEventCallback = 0;
3632 delayedEventTimerXID = 0;
3633 delayedEventCallback();
3637 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3639 if(delayedEventTimerXID && delayedEventCallback == cb)
3640 // [HGM] alive: replace, rather than add or flush identical event
3641 XtRemoveTimeOut(delayedEventTimerXID);
3642 delayedEventCallback = cb;
3643 delayedEventTimerXID =
3644 XtAppAddTimeOut(appContext, millisec,
3645 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3648 DelayedEventCallback
3651 if (delayedEventTimerXID) {
3652 return delayedEventCallback;
3659 CancelDelayedEvent ()
3661 if (delayedEventTimerXID) {
3662 XtRemoveTimeOut(delayedEventTimerXID);
3663 delayedEventTimerXID = 0;
3667 XtIntervalId loadGameTimerXID = 0;
3670 LoadGameTimerRunning ()
3672 return loadGameTimerXID != 0;
3676 StopLoadGameTimer ()
3678 if (loadGameTimerXID != 0) {
3679 XtRemoveTimeOut(loadGameTimerXID);
3680 loadGameTimerXID = 0;
3688 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3690 loadGameTimerXID = 0;
3695 StartLoadGameTimer (long millisec)
3698 XtAppAddTimeOut(appContext, millisec,
3699 (XtTimerCallbackProc) LoadGameTimerCallback,
3703 XtIntervalId analysisClockXID = 0;
3706 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3708 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3709 || appData.icsEngineAnalyze) { // [DM]
3710 AnalysisPeriodicEvent(0);
3711 StartAnalysisClock();
3716 StartAnalysisClock ()
3719 XtAppAddTimeOut(appContext, 2000,
3720 (XtTimerCallbackProc) AnalysisClockCallback,
3724 XtIntervalId clockTimerXID = 0;
3727 ClockTimerRunning ()
3729 return clockTimerXID != 0;
3735 if (clockTimerXID != 0) {
3736 XtRemoveTimeOut(clockTimerXID);
3745 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3752 StartClockTimer (long millisec)
3755 XtAppAddTimeOut(appContext, millisec,
3756 (XtTimerCallbackProc) ClockTimerCallback,
3761 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3765 Widget w = (Widget) opt->handle;
3767 /* check for low time warning */
3768 Pixel foregroundOrWarningColor = timerForegroundPixel;
3771 appData.lowTimeWarning &&
3772 (timer / 1000) < appData.icsAlarmTime)
3773 foregroundOrWarningColor = lowTimeWarningColor;
3775 if (appData.clockMode) {
3776 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
3777 XtSetArg(args[0], XtNlabel, buf);
3779 snprintf(buf, MSG_SIZ, "%s ", color);
3780 XtSetArg(args[0], XtNlabel, buf);
3785 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3786 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3788 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3789 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3792 XtSetValues(w, args, 3);
3795 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3798 SetClockIcon (int color)
3801 Pixmap pm = *clockIcons[color];
3802 if (iconPixmap != pm) {
3804 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3805 XtSetValues(shellWidget, args, 1);
3810 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3812 InputSource *is = (InputSource *) closure;
3817 if (is->lineByLine) {
3818 count = read(is->fd, is->unused,
3819 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3821 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3824 is->unused += count;
3826 while (p < is->unused) {
3827 q = memchr(p, '\n', is->unused - p);
3828 if (q == NULL) break;
3830 (is->func)(is, is->closure, p, q - p, 0);
3834 while (p < is->unused) {
3839 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3844 (is->func)(is, is->closure, is->buf, count, error);
3849 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3852 ChildProc *cp = (ChildProc *) pr;
3854 is = (InputSource *) calloc(1, sizeof(InputSource));
3855 is->lineByLine = lineByLine;
3859 is->fd = fileno(stdin);
3861 is->kind = cp->kind;
3862 is->fd = cp->fdFrom;
3865 is->unused = is->buf;
3868 is->xid = XtAppAddInput(appContext, is->fd,
3869 (XtPointer) (XtInputReadMask),
3870 (XtInputCallbackProc) DoInputCallback,
3872 is->closure = closure;
3873 return (InputSourceRef) is;
3877 RemoveInputSource (InputSourceRef isr)
3879 InputSource *is = (InputSource *) isr;
3881 if (is->xid == 0) return;
3882 XtRemoveInput(is->xid);
3886 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3888 /* Masks for XPM pieces. Black and white pieces can have
3889 different shapes, but in the interest of retaining my
3890 sanity pieces must have the same outline on both light
3891 and dark squares, and all pieces must use the same
3892 background square colors/images. */
3894 static int xpmDone = 0;
3895 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3896 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3897 static cairo_surface_t *c_animBufs[3*NrOfAnims]; // newBuf, saveBuf
3900 CreateAnimMasks (int pieceDepth)
3906 unsigned long plane;
3909 /* Need a bitmap just to get a GC with right depth */
3910 buf = XCreatePixmap(xDisplay, xBoardWindow,
3912 values.foreground = 1;
3913 values.background = 0;
3914 /* Don't use XtGetGC, not read only */
3915 maskGC = XCreateGC(xDisplay, buf,
3916 GCForeground | GCBackground, &values);
3917 XFreePixmap(xDisplay, buf);
3919 buf = XCreatePixmap(xDisplay, xBoardWindow,
3920 squareSize, squareSize, pieceDepth);
3921 values.foreground = XBlackPixel(xDisplay, xScreen);
3922 values.background = XWhitePixel(xDisplay, xScreen);
3923 bufGC = XCreateGC(xDisplay, buf,
3924 GCForeground | GCBackground, &values);
3926 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3927 /* Begin with empty mask */
3928 if(!xpmDone) // [HGM] pieces: keep using existing
3929 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3930 squareSize, squareSize, 1);
3931 XSetFunction(xDisplay, maskGC, GXclear);
3932 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3933 0, 0, squareSize, squareSize);
3935 /* Take a copy of the piece */
3940 XSetFunction(xDisplay, bufGC, GXcopy);
3941 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3943 0, 0, squareSize, squareSize, 0, 0);
3945 /* XOR the background (light) over the piece */
3946 XSetFunction(xDisplay, bufGC, GXxor);
3948 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3949 0, 0, squareSize, squareSize, 0, 0);
3951 XSetForeground(xDisplay, bufGC, lightSquareColor);
3952 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3955 /* We now have an inverted piece image with the background
3956 erased. Construct mask by just selecting all the non-zero
3957 pixels - no need to reconstruct the original image. */
3958 XSetFunction(xDisplay, maskGC, GXor);
3960 /* Might be quicker to download an XImage and create bitmap
3961 data from it rather than this N copies per piece, but it
3962 only takes a fraction of a second and there is a much
3963 longer delay for loading the pieces. */
3964 for (n = 0; n < pieceDepth; n ++) {
3965 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3966 0, 0, squareSize, squareSize,
3972 XFreePixmap(xDisplay, buf);
3973 XFreeGC(xDisplay, bufGC);
3974 XFreeGC(xDisplay, maskGC);
3978 InitAnimState (AnimNr anr, XWindowAttributes *info)
3984 DrawSeekOpen(); // set cs to board widget
3985 if(c_animBufs[anr]) cairo_surface_destroy (c_animBufs[anr]);
3986 if(c_animBufs[anr+2]) cairo_surface_destroy (c_animBufs[anr+2]);
3987 c_animBufs[anr+4] = csBoardWindow;
3988 c_animBufs[anr+2] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3989 c_animBufs[anr] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3992 /* Each buffer is square size, same depth as window */
3993 animBufs[anr+4] = xBoardWindow;
3994 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3995 squareSize, squareSize, info->depth);
3996 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3997 squareSize, squareSize, info->depth);
3999 /* Create a plain GC for blitting */
4000 mask = GCForeground | GCBackground | GCFunction |
4001 GCPlaneMask | GCGraphicsExposures;
4002 values.foreground = XBlackPixel(xDisplay, xScreen);
4003 values.background = XWhitePixel(xDisplay, xScreen);
4004 values.function = GXcopy;
4005 values.plane_mask = AllPlanes;
4006 values.graphics_exposures = False;
4007 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
4009 /* Piece will be copied from an existing context at
4010 the start of each new animation/drag. */
4011 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
4013 /* Outline will be a read-only copy of an existing */
4014 animGCs[anr+4] = None;
4020 XWindowAttributes info;
4022 if (!cairoAnimate && xpmDone && gameInfo.variant == oldVariant) return;
4023 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
4024 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
4026 InitAnimState(Game, &info);
4027 InitAnimState(Player, &info);
4029 /* For XPM pieces, we need bitmaps to use as masks. */
4030 if (useImages & !xpmDone)
4031 CreateAnimMasks(info.depth), xpmDone = 1;
4036 static Boolean frameWaiting;
4039 FrameAlarm (int sig)
4041 frameWaiting = False;
4042 /* In case System-V style signals. Needed?? */
4043 signal(SIGALRM, FrameAlarm);
4047 FrameDelay (int time)
4049 struct itimerval delay;
4051 XSync(xDisplay, False);
4054 frameWaiting = True;
4055 signal(SIGALRM, FrameAlarm);
4056 delay.it_interval.tv_sec =
4057 delay.it_value.tv_sec = time / 1000;
4058 delay.it_interval.tv_usec =
4059 delay.it_value.tv_usec = (time % 1000) * 1000;
4060 setitimer(ITIMER_REAL, &delay, NULL);
4061 while (frameWaiting) pause();
4062 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
4063 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
4064 setitimer(ITIMER_REAL, &delay, NULL);
4071 FrameDelay (int time)
4073 XSync(xDisplay, False);
4075 usleep(time * 1000);
4081 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
4085 /* Bitmap for piece being moved. */
4086 if (appData.monoMode) {
4087 *mask = *pieceToSolid(piece);
4088 } else if (useImages) {
4090 *mask = xpmMask[piece];
4092 *mask = ximMaskPm[piece];
4095 *mask = *pieceToSolid(piece);
4098 /* GC for piece being moved. Square color doesn't matter, but
4099 since it gets modified we make a copy of the original. */
4101 if (appData.monoMode)
4106 if (appData.monoMode)
4111 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
4113 /* Outline only used in mono mode and is not modified */
4115 *outline = bwPieceGC;
4117 *outline = wbPieceGC;
4121 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
4126 /* Draw solid rectangle which will be clipped to shape of piece */
4127 XFillRectangle(xDisplay, dest, clip,
4128 0, 0, squareSize, squareSize);
4129 if (appData.monoMode)
4130 /* Also draw outline in contrasting color for black
4131 on black / white on white cases */
4132 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
4133 0, 0, squareSize, squareSize, 0, 0, 1);
4135 /* Copy the piece */
4140 if(appData.upsideDown && flipView) kind ^= 2;
4141 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4143 0, 0, squareSize, squareSize,
4149 CairoOverlayPiece (ChessSquare piece, cairo_surface_t *dest)
4151 static ChessSquare oldPiece = -1;
4153 static cairo_t *pieceSource;
4154 extern int doubleClick; // in backend.c
4155 // if(piece != oldPiece || squareSize != oldSize) { // try make it faster by only changing cr if we need other piece
4156 // if(pieceSource) cairo_destroy (pieceSource);
4157 // pieceSource = cairo_create (dest);
4158 // cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
4159 // oldPiece = piece; oldSize = squareSize;
4161 pieceSource = cairo_create (dest);
4162 cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
4163 if(doubleClick) cairo_paint_with_alpha (pieceSource, 0.6);
4164 else cairo_paint(pieceSource);
4165 cairo_destroy (pieceSource);
4169 InsertPiece (AnimNr anr, ChessSquare piece)
4172 CairoOverlayPiece(piece, c_animBufs[anr]);
4174 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
4178 DrawBlank (AnimNr anr, int x, int y, int startColor)
4181 BlankSquare(x, y, startColor, EmptySquare, (Drawable) c_animBufs[anr+2], 0);
4183 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
4186 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
4187 int srcX, int srcY, int width, int height, int destX, int destY)
4190 cairo_t *cr;// = cairo_create (c_animBufs[anr+destBuf]);
4191 cr = cairo_create (c_animBufs[anr+destBuf]);
4192 if(c_animBufs[anr+srcBuf] == csBoardWindow)
4193 cairo_set_source_surface (cr, csBoardBackup, destX - srcX, destY - srcY);
4195 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
4196 cairo_rectangle (cr, destX, destY, width, height);
4199 if(c_animBufs[anr+destBuf] == csBoardWindow) {
4200 cr = cairo_create (csBoardBackup); // also draw to backup
4201 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
4202 cairo_rectangle (cr, destX, destY, width, height);
4207 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
4208 srcX, srcY, width, height, destX, destY);
4212 SetDragPiece (AnimNr anr, ChessSquare piece)
4215 if(cairoAnimate) return;
4216 /* The piece will be drawn using its own bitmap as a matte */
4217 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
4218 XSetClipMask(xDisplay, animGCs[anr+2], mask);
4221 /* [AS] Arrow highlighting support */
4223 void DrawPolygon(Pnt arrow[], int nr)
4224 { // for now on own surface; eventually this should become a global that is only destroyed on resize
4225 cairo_surface_t *boardSurface;
4228 int w = lineGap + BOARD_WIDTH * (squareSize + lineGap);
4229 int h = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
4230 boardSurface = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), w, h);
4231 cr = cairo_create (boardSurface);
4232 cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
4233 for (i=0;i<nr;i++) {
4234 cairo_line_to(cr, arrow[i].x, arrow[i].y);
4236 if(appData.monoMode) { // should we always outline arrow?
4237 cairo_line_to(cr, arrow[0].x, arrow[0].y);
4238 SetPen(cr, 2, "#000000", 0);
4239 cairo_stroke_preserve(cr);
4241 SetPen(cr, 2, appData.highlightSquareColor, 0);
4246 cairo_surface_destroy (boardSurface);
4250 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
4252 char buf[MSG_SIZ], *logoName = buf;
4253 if(appData.logo[n][0]) {
4254 logoName = appData.logo[n];
4255 } else if(appData.autoLogo) {
4256 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
4257 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
4258 } else if(appData.directory[n] && appData.directory[n][0]) {
4259 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
4263 { ASSIGN(cps->programLogo, logoName); }
4267 UpdateLogos (int displ)
4269 if(optList[W_WHITE-1].handle == NULL) return;
4270 LoadLogo(&first, 0, 0);
4271 LoadLogo(&second, 1, appData.icsActive);
4272 if(displ) DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);