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)
2538 cr = cairo_create(cs);
2539 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
2540 cairo_rectangle(cr, x, y, squareSize+lineGap, squareSize+lineGap);
2541 SetPen(cr, lineGap, type == 1 ? appData.highlightSquareColor : appData.premoveHighlightColor, 0);
2546 DrawBorder (int x, int y, int type)
2548 DoDrawBorder(csBoardWindow, x, y, type);
2549 DoDrawBorder(csBoardBackup, x, y, type);
2553 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2555 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2556 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2558 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2559 if(textureW[kind] < W*squareSize)
2560 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2562 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2563 if(textureH[kind] < H*squareSize)
2564 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2566 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2571 DrawLogo (void *handle, void *logo)
2573 cairo_surface_t *img, *cs;
2577 if(!logo || !handle) return;
2578 cs = cairo_xlib_surface_create(xDisplay, XtWindow(handle), DefaultVisual(xDisplay, 0), appData.logoSize, appData.logoSize/2);
2579 img = cairo_image_surface_create_from_png (logo);
2580 w = cairo_image_surface_get_width (img);
2581 h = cairo_image_surface_get_height (img);
2582 cr = cairo_create(cs);
2583 cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
2584 cairo_set_source_surface (cr, img, 0, 0);
2587 cairo_surface_destroy (img);
2588 cairo_surface_destroy (cs);
2592 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2593 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2595 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2596 if(pngBoardBitmap[color]) {
2598 if(!fac && !cairoAnimate) return;
2600 cr = cairo_create (fac ? csBoardWindow : (cairo_surface_t *) dest);
2601 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2602 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2603 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2607 cr = cairo_create (csBoardBackup);
2608 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2609 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2610 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2615 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2616 squareSize, squareSize, x*fac, y*fac);
2619 cairo_t *cr = cairo_create (csBoardWindow);
2622 case 0: col = appData.darkSquareColor; break;
2623 case 1: col = appData.lightSquareColor; break;
2624 case 2: col = "#000000"; break;
2626 SetPen(cr, 2.0, col, 0);
2627 cairo_rectangle (cr, x, y, squareSize, squareSize);
2630 cr = cairo_create (csBoardBackup);
2631 SetPen(cr, 2.0, col, 0);
2632 cairo_rectangle (cr, x, y, squareSize, squareSize);
2636 if (useImages && useImageSqs) {
2640 pm = xpmLightSquare;
2645 case 2: /* neutral */
2647 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2650 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2651 squareSize, squareSize, x*fac, y*fac);
2661 case 2: /* neutral */
2666 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2671 I split out the routines to draw a piece so that I could
2672 make a generic flash routine.
2675 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2677 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2678 switch (square_color) {
2680 case 2: /* neutral */
2682 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2683 ? *pieceToOutline(piece)
2684 : *pieceToSolid(piece),
2685 dest, bwPieceGC, 0, 0,
2686 squareSize, squareSize, x, y);
2689 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2690 ? *pieceToSolid(piece)
2691 : *pieceToOutline(piece),
2692 dest, wbPieceGC, 0, 0,
2693 squareSize, squareSize, x, y);
2699 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2701 switch (square_color) {
2703 case 2: /* neutral */
2705 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2706 ? *pieceToOutline(piece)
2707 : *pieceToSolid(piece),
2708 dest, bwPieceGC, 0, 0,
2709 squareSize, squareSize, x, y, 1);
2712 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2713 ? *pieceToSolid(piece)
2714 : *pieceToOutline(piece),
2715 dest, wbPieceGC, 0, 0,
2716 squareSize, squareSize, x, y, 1);
2722 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2724 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2725 switch (square_color) {
2727 XCopyPlane(xDisplay, *pieceToSolid(piece),
2728 dest, (int) piece < (int) BlackPawn
2729 ? wlPieceGC : blPieceGC, 0, 0,
2730 squareSize, squareSize, x, y, 1);
2733 XCopyPlane(xDisplay, *pieceToSolid(piece),
2734 dest, (int) piece < (int) BlackPawn
2735 ? wdPieceGC : bdPieceGC, 0, 0,
2736 squareSize, squareSize, x, y, 1);
2738 case 2: /* neutral */
2740 break; // should never contain pieces
2745 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2747 int kind, p = piece;
2749 switch (square_color) {
2751 case 2: /* neutral */
2753 if ((int)piece < (int) BlackPawn) {
2761 if ((int)piece < (int) BlackPawn) {
2769 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2770 if(useTexture & square_color+1) {
2771 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2772 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2773 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2774 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2775 XSetClipMask(xDisplay, wlPieceGC, None);
2776 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2778 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2779 dest, wlPieceGC, 0, 0,
2780 squareSize, squareSize, x, y);
2784 pngDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2786 int kind, p = piece;
2789 if ((int)piece < (int) BlackPawn) {
2795 if(appData.upsideDown && flipView) { p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2796 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2798 cr = cairo_create (csBoardWindow);
2799 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2802 cr = cairo_create (csBoardBackup);
2803 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2808 typedef void (*DrawFunc)();
2813 if (appData.monoMode) {
2814 if (DefaultDepth(xDisplay, xScreen) == 1) {
2815 return monoDrawPiece_1bit;
2817 return monoDrawPiece;
2819 } else if(appData.pngDirectory[0]) {
2820 return pngDrawPiece;
2823 return colorDrawPieceImage;
2825 return colorDrawPiece;
2830 DoDrawDot (int marker, int x, int y, int r, cairo_surface_t *cs)
2834 cr = cairo_create(cs);
2835 cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
2836 if(appData.monoMode) {
2837 SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
2838 cairo_stroke_preserve(cr);
2839 SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
2841 SetPen(cr, 2, marker == 2 ? "#FF0000" : "#FFFF00", 0);
2850 DrawDot (int marker, int x, int y, int r)
2852 DoDrawDot(marker, x, y, r, csBoardWindow);
2853 DoDrawDot(marker, x, y, r, csBoardBackup);
2857 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2858 { // basic front-end board-draw function: takes care of everything that can be in square:
2859 // piece, background, coordinate/count, marker dot
2860 int direction, font_ascent, font_descent;
2861 XCharStruct overall;
2864 if (piece == EmptySquare) {
2865 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2867 drawfunc = ChooseDrawFunc();
2868 drawfunc(piece, square_color, x, y, xBoardWindow);
2871 if(align) { // square carries inscription (coord or piece count)
2873 GC hGC = align < 3 ? coordGC : countGC;
2874 // first calculate where it goes
2875 XTextExtents(countFontStruct, string, 1, &direction,
2876 &font_ascent, &font_descent, &overall);
2878 xx += squareSize - overall.width - 2;
2879 yy += squareSize - font_descent - 1;
2880 } else if (align == 2) {
2881 xx += 2, yy += font_ascent + 1;
2882 } else if (align == 3) {
2883 xx += squareSize - overall.width - 2;
2884 yy += font_ascent + 1;
2885 } else if (align == 4) {
2886 xx += 2, yy += font_ascent + 1;
2889 if (appData.monoMode) {
2890 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2892 if(*appData.pngDirectory) {
2893 cairo_t *cr = cairo_create (csBoardWindow);
2894 cairo_select_font_face (cr, "Sans",
2895 CAIRO_FONT_SLANT_NORMAL,
2896 CAIRO_FONT_WEIGHT_BOLD);
2898 cairo_set_font_size (cr, squareSize/4);
2900 cairo_move_to (cr, xx-1, yy);
2901 if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2902 else cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
2903 cairo_show_text (cr, string);
2905 cr = cairo_create (csBoardBackup);
2906 cairo_select_font_face (cr, "Sans",
2907 CAIRO_FONT_SLANT_NORMAL,
2908 CAIRO_FONT_WEIGHT_BOLD);
2910 cairo_set_font_size (cr, squareSize/4);
2912 cairo_move_to (cr, xx-1, yy);
2913 if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2914 else cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
2915 cairo_show_text (cr, string);
2918 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2922 if(marker) { // print fat marker dot, if requested
2923 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2928 FlashDelay (int flash_delay)
2930 XSync(xDisplay, False);
2931 if(flash_delay) do_flash_delay(flash_delay);
2935 Fraction (int x, int start, int stop)
2937 double f = ((double) x - start)/(stop - start);
2938 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2942 static WindowPlacement wpNew;
2945 CoDrag (Widget sh, WindowPlacement *wp)
2948 int j=0, touch=0, fudge = 2;
2949 GetActualPlacement(sh, wp);
2950 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2951 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2952 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2953 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2954 if(!touch ) return; // only windows that touch co-move
2955 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2956 int heightInc = wpNew.height - wpMain.height;
2957 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2958 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2959 wp->y += fracTop * heightInc;
2960 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2961 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2962 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2963 int widthInc = wpNew.width - wpMain.width;
2964 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2965 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2966 wp->y += fracLeft * widthInc;
2967 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2968 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2970 wp->x += wpNew.x - wpMain.x;
2971 wp->y += wpNew.y - wpMain.y;
2972 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2973 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2974 XtSetArg(args[j], XtNx, wp->x); j++;
2975 XtSetArg(args[j], XtNy, wp->y); j++;
2976 XtSetValues(sh, args, j);
2980 ReSize (WindowPlacement *wp)
2983 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
2984 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
2985 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
2986 if(sqy < sqx) sqx = sqy;
2987 if(sqx != squareSize) {
2988 squareSize = sqx; // adopt new square size
2990 CreatePNGPieces(); // make newly scaled pieces
2991 InitDrawingSizes(0, 0); // creates grid etc.
2995 static XtIntervalId delayedDragID = 0;
3004 GetActualPlacement(shellWidget, &wpNew);
3005 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3006 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
3007 busy = 0; return; // false alarm
3010 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
3011 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3012 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
3013 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
3015 DrawPosition(True, NULL);
3016 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3024 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3026 XtAppAddTimeOut(appContext, 100, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3030 EventProc (Widget widget, caddr_t unused, XEvent *event)
3032 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
3033 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3036 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
3039 Color (char *col, int n)
3042 sscanf(col, "#%x", &c);
3048 SetPen (cairo_t *cr, float w, char *col, int dash)
3050 static const double dotted[] = {4.0, 4.0};
3051 static int len = sizeof(dotted) / sizeof(dotted[0]);
3052 cairo_set_line_width (cr, w);
3053 cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
3054 if(dash) cairo_set_dash (cr, dotted, len, 0.0);
3057 void DrawSeekAxis( int x, int y, int xTo, int yTo )
3062 cr = cairo_create (csBoardWindow);
3064 cairo_move_to (cr, x, y);
3065 cairo_line_to(cr, xTo, yTo );
3067 SetPen(cr, 2, "#000000", 0);
3074 void DrawSeekBackground( int left, int top, int right, int bottom )
3076 cairo_t *cr = cairo_create (csBoardWindow);
3078 cairo_rectangle (cr, left, top, right-left, bottom-top);
3080 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
3087 void DrawSeekText(char *buf, int x, int y)
3089 cairo_t *cr = cairo_create (csBoardWindow);
3091 cairo_select_font_face (cr, "Sans",
3092 CAIRO_FONT_SLANT_NORMAL,
3093 CAIRO_FONT_WEIGHT_NORMAL);
3095 cairo_set_font_size (cr, 12.0);
3097 cairo_move_to (cr, x, y+4);
3098 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
3099 cairo_show_text( cr, buf);
3105 void DrawSeekDot(int x, int y, int colorNr)
3107 cairo_t *cr = cairo_create (csBoardWindow);
3108 int square = colorNr & 0x80;
3112 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3114 cairo_arc(cr, x, y, squareSize/8, 0.0, 2*M_PI);
3116 SetPen(cr, 2, "#000000", 0);
3117 cairo_stroke_preserve(cr);
3119 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
3120 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
3121 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
3132 int boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3133 int boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3134 if(!csBoardWindow) {
3135 csBoardWindow = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), boardWidth, boardHeight);
3136 csBoardBackup = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, boardWidth, boardHeight);
3146 DoDrawGrid(cairo_surface_t *cs)
3148 /* draws a grid starting around Nx, Ny squares starting at x,y */
3154 cr = cairo_create (cs);
3156 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
3157 SetPen(cr, lineGap, "#000000", 0);
3160 for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
3162 cairo_move_to (cr, gridSegments[i].x1, gridSegments[i].y1);
3163 cairo_line_to (cr, gridSegments[i].x2, gridSegments[i].y2);
3176 DoDrawGrid(csBoardWindow);
3177 DoDrawGrid(csBoardBackup);
3181 * event handler for redrawing the board
3184 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3186 DrawPosition(True, NULL);
3191 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3192 { // [HGM] pv: walk PV
3193 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3196 static int savedIndex; /* gross that this is global */
3199 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3202 XawTextPosition index, dummy;
3205 XawTextGetSelectionPos(w, &index, &dummy);
3206 XtSetArg(arg, XtNstring, &val);
3207 XtGetValues(w, &arg, 1);
3208 ReplaceComment(savedIndex, val);
3209 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3210 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3214 EditCommentPopUp (int index, char *title, char *text)
3217 if (text == NULL) text = "";
3218 NewCommentPopup(title, text, index);
3222 CommentPopUp (char *title, char *text)
3224 savedIndex = currentMove; // [HGM] vari
3225 NewCommentPopup(title, text, currentMove);
3231 PopDown(CommentDlg);
3235 /* Disable all user input other than deleting the window */
3236 static int frozen = 0;
3242 /* Grab by a widget that doesn't accept input */
3243 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
3247 /* Undo a FreezeUI */
3251 if (!frozen) return;
3252 XtRemoveGrab(optList[W_MESSG].handle);
3260 static int oldPausing = FALSE;
3261 static GameMode oldmode = (GameMode) -1;
3264 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3266 if (pausing != oldPausing) {
3267 oldPausing = pausing;
3268 MarkMenuItem("Mode.Pause", pausing);
3270 if (appData.showButtonBar) {
3271 /* Always toggle, don't set. Previous code messes up when
3272 invoked while the button is pressed, as releasing it
3273 toggles the state again. */
3276 XtSetArg(args[0], XtNbackground, &oldbg);
3277 XtSetArg(args[1], XtNforeground, &oldfg);
3278 XtGetValues(optList[W_PAUSE].handle,
3280 XtSetArg(args[0], XtNbackground, oldfg);
3281 XtSetArg(args[1], XtNforeground, oldbg);
3283 XtSetValues(optList[W_PAUSE].handle, args, 2);
3287 wname = ModeToWidgetName(oldmode);
3288 if (wname != NULL) {
3289 MarkMenuItem(wname, False);
3291 wname = ModeToWidgetName(gameMode);
3292 if (wname != NULL) {
3293 MarkMenuItem(wname, True);
3296 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
3298 /* Maybe all the enables should be handled here, not just this one */
3299 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
3301 DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
3306 * Button/menu procedures
3309 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3310 char *selected_fen_position=NULL;
3313 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3314 Atom *type_return, XtPointer *value_return,
3315 unsigned long *length_return, int *format_return)
3317 char *selection_tmp;
3319 // if (!selected_fen_position) return False; /* should never happen */
3320 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3321 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3322 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3325 if (f == NULL) return False;
3329 selection_tmp = XtMalloc(len + 1);
3330 count = fread(selection_tmp, 1, len, f);
3333 XtFree(selection_tmp);
3336 selection_tmp[len] = NULLCHAR;
3338 /* note: since no XtSelectionDoneProc was registered, Xt will
3339 * automatically call XtFree on the value returned. So have to
3340 * make a copy of it allocated with XtMalloc */
3341 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3342 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3345 *value_return=selection_tmp;
3346 *length_return=strlen(selection_tmp);
3347 *type_return=*target;
3348 *format_return = 8; /* bits per byte */
3350 } else if (*target == XA_TARGETS(xDisplay)) {
3351 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3352 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3353 targets_tmp[1] = XA_STRING;
3354 *value_return = targets_tmp;
3355 *type_return = XA_ATOM;
3358 // This code leads to a read of value_return out of bounds on 64-bit systems.
3359 // Other code which I have seen always sets *format_return to 32 independent of
3360 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3361 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3362 *format_return = 8 * sizeof(Atom);
3363 if (*format_return > 32) {
3364 *length_return *= *format_return / 32;
3365 *format_return = 32;
3368 *format_return = 32;
3376 /* note: when called from menu all parameters are NULL, so no clue what the
3377 * Widget which was clicked on was, or what the click event was
3380 CopySomething (char *src)
3382 selected_fen_position = src;
3384 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3385 * have a notion of a position that is selected but not copied.
3386 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3388 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3390 SendPositionSelection,
3391 NULL/* lose_ownership_proc */ ,
3392 NULL/* transfer_done_proc */);
3393 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3395 SendPositionSelection,
3396 NULL/* lose_ownership_proc */ ,
3397 NULL/* transfer_done_proc */);
3400 /* function called when the data to Paste is ready */
3402 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3403 Atom *type, XtPointer value, unsigned long *len, int *format)
3406 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3407 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3408 EditPositionPasteFEN(fenstr);
3412 /* called when Paste Position button is pressed,
3413 * all parameters will be NULL */
3415 PastePositionProc ()
3417 XtGetSelectionValue(menuBarWidget,
3418 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3419 /* (XtSelectionCallbackProc) */ PastePositionCB,
3420 NULL, /* client_data passed to PastePositionCB */
3422 /* better to use the time field from the event that triggered the
3423 * call to this function, but that isn't trivial to get
3430 /* note: when called from menu all parameters are NULL, so no clue what the
3431 * Widget which was clicked on was, or what the click event was
3433 /* function called when the data to Paste is ready */
3435 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3436 Atom *type, XtPointer value, unsigned long *len, int *format)
3439 if (value == NULL || *len == 0) {
3440 return; /* nothing had been selected to copy */
3442 f = fopen(gamePasteFilename, "w");
3444 DisplayError(_("Can't open temp file"), errno);
3447 fwrite(value, 1, *len, f);
3450 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3453 /* called when Paste Game button is pressed,
3454 * all parameters will be NULL */
3458 XtGetSelectionValue(menuBarWidget,
3459 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3460 /* (XtSelectionCallbackProc) */ PasteGameCB,
3461 NULL, /* client_data passed to PasteGameCB */
3463 /* better to use the time field from the event that triggered the
3464 * call to this function, but that isn't trivial to get
3473 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3480 { // bassic primitive for determining if modifier keys are pressed
3481 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3484 XQueryKeymap(xDisplay,keys);
3485 for(i=0; i<6; i++) {
3487 j = XKeysymToKeycode(xDisplay, codes[i]);
3488 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3494 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3498 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3499 if ( n == 1 && *buf >= 32 // printable
3500 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3501 ) BoxAutoPopUp (buf);
3505 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3506 { // [HGM] input: let up-arrow recall previous line from history
3511 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3512 { // [HGM] input: let down-arrow recall next line from history
3517 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3523 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3525 if (!TempBackwardActive) {
3526 TempBackwardActive = True;
3532 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3534 /* Check to see if triggered by a key release event for a repeating key.
3535 * If so the next queued event will be a key press of the same key at the same time */
3536 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3538 XPeekEvent(xDisplay, &next);
3539 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3540 next.xkey.keycode == event->xkey.keycode)
3544 TempBackwardActive = False;
3548 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3549 { // called as key binding
3552 if (nprms && *nprms > 0)
3556 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3562 { // called from menu
3563 ManInner(NULL, NULL, NULL, NULL);
3567 SetWindowTitle (char *text, char *title, char *icon)
3571 if (appData.titleInWindow) {
3573 XtSetArg(args[i], XtNlabel, text); i++;
3574 XtSetValues(titleWidget, args, i);
3577 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3578 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3579 XtSetValues(shellWidget, args, i);
3580 XSync(xDisplay, False);
3585 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3591 DisplayIcsInteractionTitle (String message)
3593 if (oldICSInteractionTitle == NULL) {
3594 /* Magic to find the old window title, adapted from vim */
3595 char *wina = getenv("WINDOWID");
3597 Window win = (Window) atoi(wina);
3598 Window root, parent, *children;
3599 unsigned int nchildren;
3600 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3602 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3603 if (!XQueryTree(xDisplay, win, &root, &parent,
3604 &children, &nchildren)) break;
3605 if (children) XFree((void *)children);
3606 if (parent == root || parent == 0) break;
3609 XSetErrorHandler(oldHandler);
3611 if (oldICSInteractionTitle == NULL) {
3612 oldICSInteractionTitle = "xterm";
3615 printf("\033]0;%s\007", message);
3620 XtIntervalId delayedEventTimerXID = 0;
3621 DelayedEventCallback delayedEventCallback = 0;
3626 delayedEventTimerXID = 0;
3627 delayedEventCallback();
3631 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3633 if(delayedEventTimerXID && delayedEventCallback == cb)
3634 // [HGM] alive: replace, rather than add or flush identical event
3635 XtRemoveTimeOut(delayedEventTimerXID);
3636 delayedEventCallback = cb;
3637 delayedEventTimerXID =
3638 XtAppAddTimeOut(appContext, millisec,
3639 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3642 DelayedEventCallback
3645 if (delayedEventTimerXID) {
3646 return delayedEventCallback;
3653 CancelDelayedEvent ()
3655 if (delayedEventTimerXID) {
3656 XtRemoveTimeOut(delayedEventTimerXID);
3657 delayedEventTimerXID = 0;
3661 XtIntervalId loadGameTimerXID = 0;
3664 LoadGameTimerRunning ()
3666 return loadGameTimerXID != 0;
3670 StopLoadGameTimer ()
3672 if (loadGameTimerXID != 0) {
3673 XtRemoveTimeOut(loadGameTimerXID);
3674 loadGameTimerXID = 0;
3682 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3684 loadGameTimerXID = 0;
3689 StartLoadGameTimer (long millisec)
3692 XtAppAddTimeOut(appContext, millisec,
3693 (XtTimerCallbackProc) LoadGameTimerCallback,
3697 XtIntervalId analysisClockXID = 0;
3700 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3702 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3703 || appData.icsEngineAnalyze) { // [DM]
3704 AnalysisPeriodicEvent(0);
3705 StartAnalysisClock();
3710 StartAnalysisClock ()
3713 XtAppAddTimeOut(appContext, 2000,
3714 (XtTimerCallbackProc) AnalysisClockCallback,
3718 XtIntervalId clockTimerXID = 0;
3721 ClockTimerRunning ()
3723 return clockTimerXID != 0;
3729 if (clockTimerXID != 0) {
3730 XtRemoveTimeOut(clockTimerXID);
3739 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3746 StartClockTimer (long millisec)
3749 XtAppAddTimeOut(appContext, millisec,
3750 (XtTimerCallbackProc) ClockTimerCallback,
3755 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3759 Widget w = (Widget) opt->handle;
3761 /* check for low time warning */
3762 Pixel foregroundOrWarningColor = timerForegroundPixel;
3765 appData.lowTimeWarning &&
3766 (timer / 1000) < appData.icsAlarmTime)
3767 foregroundOrWarningColor = lowTimeWarningColor;
3769 if (appData.clockMode) {
3770 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
3771 XtSetArg(args[0], XtNlabel, buf);
3773 snprintf(buf, MSG_SIZ, "%s ", color);
3774 XtSetArg(args[0], XtNlabel, buf);
3779 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3780 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3782 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3783 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3786 XtSetValues(w, args, 3);
3789 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3792 SetClockIcon (int color)
3795 Pixmap pm = *clockIcons[color];
3796 if (iconPixmap != pm) {
3798 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3799 XtSetValues(shellWidget, args, 1);
3804 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3806 InputSource *is = (InputSource *) closure;
3811 if (is->lineByLine) {
3812 count = read(is->fd, is->unused,
3813 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3815 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3818 is->unused += count;
3820 while (p < is->unused) {
3821 q = memchr(p, '\n', is->unused - p);
3822 if (q == NULL) break;
3824 (is->func)(is, is->closure, p, q - p, 0);
3828 while (p < is->unused) {
3833 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3838 (is->func)(is, is->closure, is->buf, count, error);
3843 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3846 ChildProc *cp = (ChildProc *) pr;
3848 is = (InputSource *) calloc(1, sizeof(InputSource));
3849 is->lineByLine = lineByLine;
3853 is->fd = fileno(stdin);
3855 is->kind = cp->kind;
3856 is->fd = cp->fdFrom;
3859 is->unused = is->buf;
3862 is->xid = XtAppAddInput(appContext, is->fd,
3863 (XtPointer) (XtInputReadMask),
3864 (XtInputCallbackProc) DoInputCallback,
3866 is->closure = closure;
3867 return (InputSourceRef) is;
3871 RemoveInputSource (InputSourceRef isr)
3873 InputSource *is = (InputSource *) isr;
3875 if (is->xid == 0) return;
3876 XtRemoveInput(is->xid);
3880 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3882 /* Masks for XPM pieces. Black and white pieces can have
3883 different shapes, but in the interest of retaining my
3884 sanity pieces must have the same outline on both light
3885 and dark squares, and all pieces must use the same
3886 background square colors/images. */
3888 static int xpmDone = 0;
3889 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3890 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3891 static cairo_surface_t *c_animBufs[3*NrOfAnims]; // newBuf, saveBuf
3894 CreateAnimMasks (int pieceDepth)
3900 unsigned long plane;
3903 /* Need a bitmap just to get a GC with right depth */
3904 buf = XCreatePixmap(xDisplay, xBoardWindow,
3906 values.foreground = 1;
3907 values.background = 0;
3908 /* Don't use XtGetGC, not read only */
3909 maskGC = XCreateGC(xDisplay, buf,
3910 GCForeground | GCBackground, &values);
3911 XFreePixmap(xDisplay, buf);
3913 buf = XCreatePixmap(xDisplay, xBoardWindow,
3914 squareSize, squareSize, pieceDepth);
3915 values.foreground = XBlackPixel(xDisplay, xScreen);
3916 values.background = XWhitePixel(xDisplay, xScreen);
3917 bufGC = XCreateGC(xDisplay, buf,
3918 GCForeground | GCBackground, &values);
3920 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3921 /* Begin with empty mask */
3922 if(!xpmDone) // [HGM] pieces: keep using existing
3923 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3924 squareSize, squareSize, 1);
3925 XSetFunction(xDisplay, maskGC, GXclear);
3926 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3927 0, 0, squareSize, squareSize);
3929 /* Take a copy of the piece */
3934 XSetFunction(xDisplay, bufGC, GXcopy);
3935 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3937 0, 0, squareSize, squareSize, 0, 0);
3939 /* XOR the background (light) over the piece */
3940 XSetFunction(xDisplay, bufGC, GXxor);
3942 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3943 0, 0, squareSize, squareSize, 0, 0);
3945 XSetForeground(xDisplay, bufGC, lightSquareColor);
3946 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3949 /* We now have an inverted piece image with the background
3950 erased. Construct mask by just selecting all the non-zero
3951 pixels - no need to reconstruct the original image. */
3952 XSetFunction(xDisplay, maskGC, GXor);
3954 /* Might be quicker to download an XImage and create bitmap
3955 data from it rather than this N copies per piece, but it
3956 only takes a fraction of a second and there is a much
3957 longer delay for loading the pieces. */
3958 for (n = 0; n < pieceDepth; n ++) {
3959 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3960 0, 0, squareSize, squareSize,
3966 XFreePixmap(xDisplay, buf);
3967 XFreeGC(xDisplay, bufGC);
3968 XFreeGC(xDisplay, maskGC);
3972 InitAnimState (AnimNr anr, XWindowAttributes *info)
3978 DrawSeekOpen(); // set cs to board widget
3979 if(c_animBufs[anr]) cairo_surface_destroy (c_animBufs[anr]);
3980 if(c_animBufs[anr+2]) cairo_surface_destroy (c_animBufs[anr+2]);
3981 c_animBufs[anr+4] = csBoardWindow;
3982 c_animBufs[anr+2] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3983 c_animBufs[anr] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3986 /* Each buffer is square size, same depth as window */
3987 animBufs[anr+4] = xBoardWindow;
3988 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3989 squareSize, squareSize, info->depth);
3990 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3991 squareSize, squareSize, info->depth);
3993 /* Create a plain GC for blitting */
3994 mask = GCForeground | GCBackground | GCFunction |
3995 GCPlaneMask | GCGraphicsExposures;
3996 values.foreground = XBlackPixel(xDisplay, xScreen);
3997 values.background = XWhitePixel(xDisplay, xScreen);
3998 values.function = GXcopy;
3999 values.plane_mask = AllPlanes;
4000 values.graphics_exposures = False;
4001 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
4003 /* Piece will be copied from an existing context at
4004 the start of each new animation/drag. */
4005 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
4007 /* Outline will be a read-only copy of an existing */
4008 animGCs[anr+4] = None;
4014 XWindowAttributes info;
4016 if (!cairoAnimate && xpmDone && gameInfo.variant == oldVariant) return;
4017 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
4018 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
4020 InitAnimState(Game, &info);
4021 InitAnimState(Player, &info);
4023 /* For XPM pieces, we need bitmaps to use as masks. */
4024 if (useImages & !xpmDone)
4025 CreateAnimMasks(info.depth), xpmDone = 1;
4030 static Boolean frameWaiting;
4033 FrameAlarm (int sig)
4035 frameWaiting = False;
4036 /* In case System-V style signals. Needed?? */
4037 signal(SIGALRM, FrameAlarm);
4041 FrameDelay (int time)
4043 struct itimerval delay;
4045 XSync(xDisplay, False);
4048 frameWaiting = True;
4049 signal(SIGALRM, FrameAlarm);
4050 delay.it_interval.tv_sec =
4051 delay.it_value.tv_sec = time / 1000;
4052 delay.it_interval.tv_usec =
4053 delay.it_value.tv_usec = (time % 1000) * 1000;
4054 setitimer(ITIMER_REAL, &delay, NULL);
4055 while (frameWaiting) pause();
4056 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
4057 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
4058 setitimer(ITIMER_REAL, &delay, NULL);
4065 FrameDelay (int time)
4067 XSync(xDisplay, False);
4069 usleep(time * 1000);
4075 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
4079 /* Bitmap for piece being moved. */
4080 if (appData.monoMode) {
4081 *mask = *pieceToSolid(piece);
4082 } else if (useImages) {
4084 *mask = xpmMask[piece];
4086 *mask = ximMaskPm[piece];
4089 *mask = *pieceToSolid(piece);
4092 /* GC for piece being moved. Square color doesn't matter, but
4093 since it gets modified we make a copy of the original. */
4095 if (appData.monoMode)
4100 if (appData.monoMode)
4105 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
4107 /* Outline only used in mono mode and is not modified */
4109 *outline = bwPieceGC;
4111 *outline = wbPieceGC;
4115 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
4120 /* Draw solid rectangle which will be clipped to shape of piece */
4121 XFillRectangle(xDisplay, dest, clip,
4122 0, 0, squareSize, squareSize);
4123 if (appData.monoMode)
4124 /* Also draw outline in contrasting color for black
4125 on black / white on white cases */
4126 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
4127 0, 0, squareSize, squareSize, 0, 0, 1);
4129 /* Copy the piece */
4134 if(appData.upsideDown && flipView) kind ^= 2;
4135 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4137 0, 0, squareSize, squareSize,
4143 CairoOverlayPiece (ChessSquare piece, cairo_surface_t *dest)
4145 static ChessSquare oldPiece = -1;
4147 static cairo_t *pieceSource;
4148 extern int doubleClick; // in backend.c
4149 // if(piece != oldPiece || squareSize != oldSize) { // try make it faster by only changing cr if we need other piece
4150 // if(pieceSource) cairo_destroy (pieceSource);
4151 // pieceSource = cairo_create (dest);
4152 // cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
4153 // oldPiece = piece; oldSize = squareSize;
4155 pieceSource = cairo_create (dest);
4156 cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
4157 if(doubleClick) cairo_paint_with_alpha (pieceSource, 0.6);
4158 else cairo_paint(pieceSource);
4159 cairo_destroy (pieceSource);
4163 InsertPiece (AnimNr anr, ChessSquare piece)
4166 CairoOverlayPiece(piece, c_animBufs[anr]);
4168 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
4172 DrawBlank (AnimNr anr, int x, int y, int startColor)
4175 BlankSquare(x, y, startColor, EmptySquare, (Drawable) c_animBufs[anr+2], 0);
4177 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
4180 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
4181 int srcX, int srcY, int width, int height, int destX, int destY)
4184 cairo_t *cr;// = cairo_create (c_animBufs[anr+destBuf]);
4185 cr = cairo_create (c_animBufs[anr+destBuf]);
4186 if(c_animBufs[anr+srcBuf] == csBoardWindow)
4187 cairo_set_source_surface (cr, csBoardBackup, destX - srcX, destY - srcY);
4189 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
4190 cairo_rectangle (cr, destX, destY, width, height);
4193 if(c_animBufs[anr+destBuf] == csBoardWindow) {
4194 cr = cairo_create (csBoardBackup); // also draw to backup
4195 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
4196 cairo_rectangle (cr, destX, destY, width, height);
4201 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
4202 srcX, srcY, width, height, destX, destY);
4206 SetDragPiece (AnimNr anr, ChessSquare piece)
4209 if(cairoAnimate) return;
4210 /* The piece will be drawn using its own bitmap as a matte */
4211 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
4212 XSetClipMask(xDisplay, animGCs[anr+2], mask);
4215 /* [AS] Arrow highlighting support */
4217 void DrawPolygon(Pnt arrow[], int nr)
4218 { // for now on own surface; eventually this should become a global that is only destroyed on resize
4219 cairo_surface_t *boardSurface;
4222 int w = lineGap + BOARD_WIDTH * (squareSize + lineGap);
4223 int h = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
4224 boardSurface = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), w, h);
4225 cr = cairo_create (boardSurface);
4226 cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
4227 for (i=0;i<nr;i++) {
4228 cairo_line_to(cr, arrow[i].x, arrow[i].y);
4230 if(appData.monoMode) { // should we always outline arrow?
4231 cairo_line_to(cr, arrow[0].x, arrow[0].y);
4232 SetPen(cr, 2, "#000000", 0);
4233 cairo_stroke_preserve(cr);
4235 SetPen(cr, 2, appData.highlightSquareColor, 0);
4240 cairo_surface_destroy (boardSurface);
4244 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
4246 char buf[MSG_SIZ], *logoName = buf;
4247 if(appData.logo[n][0]) {
4248 logoName = appData.logo[n];
4249 } else if(appData.autoLogo) {
4250 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
4251 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
4252 } else if(appData.directory[n] && appData.directory[n][0]) {
4253 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
4257 { ASSIGN(cps->programLogo, logoName); }
4261 UpdateLogos (int displ)
4263 if(optList[W_WHITE-1].handle == NULL) return;
4264 LoadLogo(&first, 0, 0);
4265 LoadLogo(&second, 1, appData.icsActive);
4266 if(displ) DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);