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
1061 oldMono = -10; // kludge to force recreation of animation masks
1062 oldVariant = gameInfo.variant;
1065 if(appData.monoMode != oldMono || cairoAnimate)
1068 oldMono = appData.monoMode;
1072 MakeOneColor (char *name, Pixel *color)
1074 XrmValue vFrom, vTo;
1075 if (!appData.monoMode) {
1076 vFrom.addr = (caddr_t) name;
1077 vFrom.size = strlen(name);
1078 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1079 if (vTo.addr == NULL) {
1080 appData.monoMode = True;
1083 *color = *(Pixel *) vTo.addr;
1091 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1092 int forceMono = False;
1094 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1095 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1096 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1097 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1098 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1099 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1100 if (appData.lowTimeWarning)
1101 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1102 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1103 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1110 { // [HGM] taken out of main
1112 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1113 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1114 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1116 if (appData.bitmapDirectory[0] != NULLCHAR) {
1120 CreateXPMBoard(appData.liteBackTextureFile, 1);
1121 CreateXPMBoard(appData.darkBackTextureFile, 0);
1123 if (appData.pngDirectory[0] != NULLCHAR) { // for now do in parallel
1128 /* Create regular pieces */
1129 if (!useImages) CreatePieces();
1134 InitDrawingParams ()
1136 MakeColors(); CreateGCs(True);
1141 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1142 { // detervtomine what fonts to use, and create them
1146 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1147 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1148 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1149 appData.font = fontTable[MESSAGE_FONT][squareSize];
1150 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1151 appData.coordFont = fontTable[COORD_FONT][squareSize];
1154 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1155 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1156 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1157 fontSet = CreateFontSet(appData.font);
1158 clockFontSet = CreateFontSet(appData.clockFont);
1160 /* For the coordFont, use the 0th font of the fontset. */
1161 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1162 XFontStruct **font_struct_list;
1163 XFontSetExtents *fontSize;
1164 char **font_name_list;
1165 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1166 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1167 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1168 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1169 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1172 appData.font = FindFont(appData.font, fontPxlSize);
1173 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1174 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1175 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1176 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1177 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1178 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1179 // textHeight in !NLS mode!
1181 countFontID = coordFontID; // [HGM] holdings
1182 countFontStruct = coordFontStruct;
1184 xdb = XtDatabase(xDisplay);
1186 XrmPutLineResource(&xdb, "*international: True");
1187 vTo.size = sizeof(XFontSet);
1188 vTo.addr = (XtPointer) &fontSet;
1189 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1191 XrmPutStringResource(&xdb, "*font", appData.font);
1196 PrintArg (ArgType t)
1201 case ArgInt: p = " N"; break;
1202 case ArgString: p = " STR"; break;
1203 case ArgBoolean: p = " TF"; break;
1204 case ArgSettingsFilename:
1205 case ArgFilename: p = " FILE"; break;
1206 case ArgX: p = " Nx"; break;
1207 case ArgY: p = " Ny"; break;
1208 case ArgAttribs: p = " TEXTCOL"; break;
1209 case ArgColor: p = " COL"; break;
1210 case ArgFont: p = " FONT"; break;
1211 case ArgBoardSize: p = " SIZE"; break;
1212 case ArgFloat: p = " FLOAT"; break;
1217 case ArgCommSettings:
1228 ArgDescriptor *q, *p = argDescriptors+5;
1229 printf("\nXBoard accepts the following options:\n"
1230 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1231 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1232 " SIZE = board-size spec(s)\n"
1233 " Within parentheses are short forms, or options to set to true or false.\n"
1234 " Persistent options (saved in the settings file) are marked with *)\n\n");
1236 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1237 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1238 if(p->save) strcat(buf+len, "*");
1239 for(q=p+1; q->argLoc == p->argLoc; q++) {
1240 if(q->argName[0] == '-') continue;
1241 strcat(buf+len, q == p+1 ? " (" : " ");
1242 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1244 if(q != p+1) strcat(buf+len, ")");
1246 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1249 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1253 main (int argc, char **argv)
1255 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1256 XSetWindowAttributes window_attributes;
1258 Dimension boardWidth, boardHeight, w, h;
1260 int forceMono = False;
1262 srandom(time(0)); // [HGM] book: make random truly random
1264 setbuf(stdout, NULL);
1265 setbuf(stderr, NULL);
1268 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1269 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1273 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1278 programName = strrchr(argv[0], '/');
1279 if (programName == NULL)
1280 programName = argv[0];
1285 XtSetLanguageProc(NULL, NULL, NULL);
1286 if (appData.debugMode) {
1287 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1290 bindtextdomain(PACKAGE, LOCALEDIR);
1291 textdomain(PACKAGE);
1294 appData.boardSize = "";
1295 InitAppData(ConvertToLine(argc, argv));
1297 if (p == NULL) p = "/tmp";
1298 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1299 gameCopyFilename = (char*) malloc(i);
1300 gamePasteFilename = (char*) malloc(i);
1301 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1302 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1304 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1305 static char buf[MSG_SIZ];
1306 EscapeExpand(buf, appData.firstInitString);
1307 appData.firstInitString = strdup(buf);
1308 EscapeExpand(buf, appData.secondInitString);
1309 appData.secondInitString = strdup(buf);
1310 EscapeExpand(buf, appData.firstComputerString);
1311 appData.firstComputerString = strdup(buf);
1312 EscapeExpand(buf, appData.secondComputerString);
1313 appData.secondComputerString = strdup(buf);
1316 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1319 if (chdir(chessDir) != 0) {
1320 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1326 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1327 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1328 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1329 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1332 setbuf(debugFP, NULL);
1335 /* [HGM,HR] make sure board size is acceptable */
1336 if(appData.NrFiles > BOARD_FILES ||
1337 appData.NrRanks > BOARD_RANKS )
1338 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1341 /* This feature does not work; animation needs a rewrite */
1342 appData.highlightDragging = FALSE;
1346 gameInfo.variant = StringToVariant(appData.variant);
1347 InitPosition(FALSE);
1350 XtAppInitialize(&appContext, "XBoard", shellOptions,
1351 XtNumber(shellOptions),
1352 &argc, argv, xboardResources, NULL, 0);
1354 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1355 clientResources, XtNumber(clientResources),
1358 xDisplay = XtDisplay(shellWidget);
1359 xScreen = DefaultScreen(xDisplay);
1360 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1363 * determine size, based on supplied or remembered -size, or screen size
1365 if (isdigit(appData.boardSize[0])) {
1366 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1367 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1368 &fontPxlSize, &smallLayout, &tinyLayout);
1370 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1371 programName, appData.boardSize);
1375 /* Find some defaults; use the nearest known size */
1376 SizeDefaults *szd, *nearest;
1377 int distance = 99999;
1378 nearest = szd = sizeDefaults;
1379 while (szd->name != NULL) {
1380 if (abs(szd->squareSize - squareSize) < distance) {
1382 distance = abs(szd->squareSize - squareSize);
1383 if (distance == 0) break;
1387 if (i < 2) lineGap = nearest->lineGap;
1388 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1389 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1390 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1391 if (i < 6) smallLayout = nearest->smallLayout;
1392 if (i < 7) tinyLayout = nearest->tinyLayout;
1395 SizeDefaults *szd = sizeDefaults;
1396 if (*appData.boardSize == NULLCHAR) {
1397 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1398 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1401 if (szd->name == NULL) szd--;
1402 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1404 while (szd->name != NULL &&
1405 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1406 if (szd->name == NULL) {
1407 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1408 programName, appData.boardSize);
1412 squareSize = szd->squareSize;
1413 lineGap = szd->lineGap;
1414 clockFontPxlSize = szd->clockFontPxlSize;
1415 coordFontPxlSize = szd->coordFontPxlSize;
1416 fontPxlSize = szd->fontPxlSize;
1417 smallLayout = szd->smallLayout;
1418 tinyLayout = szd->tinyLayout;
1419 // [HGM] font: use defaults from settings file if available and not overruled
1422 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1423 if (strlen(appData.pixmapDirectory) > 0) {
1424 p = ExpandPathName(appData.pixmapDirectory);
1426 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1427 appData.pixmapDirectory);
1430 if (appData.debugMode) {
1431 fprintf(stderr, _("\
1432 XBoard square size (hint): %d\n\
1433 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1435 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1436 if (appData.debugMode) {
1437 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1440 defaultLineGap = lineGap;
1441 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1443 /* [HR] height treated separately (hacked) */
1444 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1445 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1448 * Determine what fonts to use.
1450 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1453 * Detect if there are not enough colors available and adapt.
1455 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1456 appData.monoMode = True;
1459 forceMono = MakeColors();
1462 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1464 appData.monoMode = True;
1467 if (appData.monoMode && appData.debugMode) {
1468 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1469 (unsigned long) XWhitePixel(xDisplay, xScreen),
1470 (unsigned long) XBlackPixel(xDisplay, xScreen));
1473 ParseIcsTextColors();
1475 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1481 layoutName = "tinyLayout";
1482 } else if (smallLayout) {
1483 layoutName = "smallLayout";
1485 layoutName = "normalLayout";
1488 optList = BoardPopUp(squareSize, lineGap, (void*)
1494 boardWidget = optList[W_BOARD].handle;
1495 menuBarWidget = optList[W_MENU].handle;
1496 dropMenu = optList[W_DROP].handle;
1497 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1498 formWidget = XtParent(boardWidget);
1499 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1500 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1501 XtGetValues(optList[W_WHITE].handle, args, 2);
1502 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1503 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1504 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1505 XtGetValues(optList[W_PAUSE].handle, args, 2);
1507 AppendEnginesToMenu(appData.recentEngineList);
1509 xBoardWindow = XtWindow(boardWidget);
1511 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1512 // not need to go into InitDrawingSizes().
1515 * Create X checkmark bitmap and initialize option menu checks.
1517 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1518 checkmark_bits, checkmark_width, checkmark_height);
1524 ReadBitmap(&wIconPixmap, "icon_white.bm",
1525 icon_white_bits, icon_white_width, icon_white_height);
1526 ReadBitmap(&bIconPixmap, "icon_black.bm",
1527 icon_black_bits, icon_black_width, icon_black_height);
1528 iconPixmap = wIconPixmap;
1530 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1531 XtSetValues(shellWidget, args, i);
1534 * Create a cursor for the board widget.
1536 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1537 XChangeWindowAttributes(xDisplay, xBoardWindow,
1538 CWCursor, &window_attributes);
1541 * Inhibit shell resizing.
1545 cairoAnimate = *appData.pngDirectory && useTexture == 3
1546 && strstr(appData.liteBackTextureFile, ".png") && strstr(appData.darkBackTextureFile, ".png");
1548 shellArgs[0].value = (XtArgVal) &w;
1549 shellArgs[1].value = (XtArgVal) &h;
1550 XtGetValues(shellWidget, shellArgs, 2);
1551 shellArgs[4].value = shellArgs[2].value = w;
1552 shellArgs[5].value = shellArgs[3].value = h;
1553 if(!cairoAnimate) XtSetValues(shellWidget, &shellArgs[2], 4);
1554 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1555 marginH = h - boardHeight;
1557 CatchDeleteWindow(shellWidget, "QuitProc");
1562 if(appData.logoSize)
1563 { // locate and read user logo
1565 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1566 ASSIGN(userLogo, buf);
1569 if (appData.animate || appData.animateDragging)
1572 XtAugmentTranslations(formWidget,
1573 XtParseTranslationTable(globalTranslations));
1575 XtAddEventHandler(formWidget, KeyPressMask, False,
1576 (XtEventHandler) MoveTypeInProc, NULL);
1577 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1578 (XtEventHandler) EventProc, NULL);
1580 /* [AS] Restore layout */
1581 if( wpMoveHistory.visible ) {
1585 if( wpEvalGraph.visible )
1590 if( wpEngineOutput.visible ) {
1591 EngineOutputPopUp();
1596 if (errorExitStatus == -1) {
1597 if (appData.icsActive) {
1598 /* We now wait until we see "login:" from the ICS before
1599 sending the logon script (problems with timestamp otherwise) */
1600 /*ICSInitScript();*/
1601 if (appData.icsInputBox) ICSInputBoxPopUp();
1605 signal(SIGWINCH, TermSizeSigHandler);
1607 signal(SIGINT, IntSigHandler);
1608 signal(SIGTERM, IntSigHandler);
1609 if (*appData.cmailGameName != NULLCHAR) {
1610 signal(SIGUSR1, CmailSigHandler);
1614 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1617 // XtSetKeyboardFocus(shellWidget, formWidget);
1618 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1620 XtAppMainLoop(appContext);
1621 if (appData.debugMode) fclose(debugFP); // [DM] debug
1626 TermSizeSigHandler (int sig)
1632 IntSigHandler (int sig)
1638 CmailSigHandler (int sig)
1643 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1645 /* Activate call-back function CmailSigHandlerCallBack() */
1646 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1648 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1652 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1655 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1657 /**** end signal code ****/
1660 #define Abs(n) ((n)<0 ? -(n) : (n))
1664 InsertPxlSize (char *pattern, int targetPxlSize)
1666 char *base_fnt_lst, strInt[12], *p, *q;
1667 int alternatives, i, len, strIntLen;
1670 * Replace the "*" (if present) in the pixel-size slot of each
1671 * alternative with the targetPxlSize.
1675 while ((p = strchr(p, ',')) != NULL) {
1679 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1680 strIntLen = strlen(strInt);
1681 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1685 while (alternatives--) {
1686 char *comma = strchr(p, ',');
1687 for (i=0; i<14; i++) {
1688 char *hyphen = strchr(p, '-');
1690 if (comma && hyphen > comma) break;
1691 len = hyphen + 1 - p;
1692 if (i == 7 && *p == '*' && len == 2) {
1694 memcpy(q, strInt, strIntLen);
1704 len = comma + 1 - p;
1711 return base_fnt_lst;
1715 CreateFontSet (char *base_fnt_lst)
1718 char **missing_list;
1722 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1723 &missing_list, &missing_count, &def_string);
1724 if (appData.debugMode) {
1726 XFontStruct **font_struct_list;
1727 char **font_name_list;
1728 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1730 fprintf(debugFP, " got list %s, locale %s\n",
1731 XBaseFontNameListOfFontSet(fntSet),
1732 XLocaleOfFontSet(fntSet));
1733 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1734 for (i = 0; i < count; i++) {
1735 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1738 for (i = 0; i < missing_count; i++) {
1739 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1742 if (fntSet == NULL) {
1743 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1748 #else // not ENABLE_NLS
1750 * Find a font that matches "pattern" that is as close as
1751 * possible to the targetPxlSize. Prefer fonts that are k
1752 * pixels smaller to fonts that are k pixels larger. The
1753 * pattern must be in the X Consortium standard format,
1754 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1755 * The return value should be freed with XtFree when no
1759 FindFont (char *pattern, int targetPxlSize)
1761 char **fonts, *p, *best, *scalable, *scalableTail;
1762 int i, j, nfonts, minerr, err, pxlSize;
1764 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1766 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1767 programName, pattern);
1774 for (i=0; i<nfonts; i++) {
1777 if (*p != '-') continue;
1779 if (*p == NULLCHAR) break;
1780 if (*p++ == '-') j++;
1782 if (j < 7) continue;
1785 scalable = fonts[i];
1788 err = pxlSize - targetPxlSize;
1789 if (Abs(err) < Abs(minerr) ||
1790 (minerr > 0 && err < 0 && -err == minerr)) {
1796 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1797 /* If the error is too big and there is a scalable font,
1798 use the scalable font. */
1799 int headlen = scalableTail - scalable;
1800 p = (char *) XtMalloc(strlen(scalable) + 10);
1801 while (isdigit(*scalableTail)) scalableTail++;
1802 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1804 p = (char *) XtMalloc(strlen(best) + 2);
1805 safeStrCpy(p, best, strlen(best)+1 );
1807 if (appData.debugMode) {
1808 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1809 pattern, targetPxlSize, p);
1811 XFreeFontNames(fonts);
1818 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1819 // must be called before all non-first callse to CreateGCs()
1820 XtReleaseGC(shellWidget, highlineGC);
1821 XtReleaseGC(shellWidget, lightSquareGC);
1822 XtReleaseGC(shellWidget, darkSquareGC);
1823 XtReleaseGC(shellWidget, lineGC);
1824 if (appData.monoMode) {
1825 if (DefaultDepth(xDisplay, xScreen) == 1) {
1826 XtReleaseGC(shellWidget, wbPieceGC);
1828 XtReleaseGC(shellWidget, bwPieceGC);
1831 XtReleaseGC(shellWidget, prelineGC);
1832 XtReleaseGC(shellWidget, wdPieceGC);
1833 XtReleaseGC(shellWidget, wlPieceGC);
1834 XtReleaseGC(shellWidget, bdPieceGC);
1835 XtReleaseGC(shellWidget, blPieceGC);
1840 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1842 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1843 | GCBackground | GCFunction | GCPlaneMask;
1844 gc_values->foreground = foreground;
1845 gc_values->background = background;
1846 return XtGetGC(shellWidget, value_mask, gc_values);
1850 CreateGCs (int redo)
1852 XGCValues gc_values;
1854 Pixel white = XWhitePixel(xDisplay, xScreen);
1855 Pixel black = XBlackPixel(xDisplay, xScreen);
1857 gc_values.plane_mask = AllPlanes;
1858 gc_values.line_width = lineGap;
1859 gc_values.line_style = LineSolid;
1860 gc_values.function = GXcopy;
1863 DeleteGCs(); // called a second time; clean up old GCs first
1864 } else { // [HGM] grid and font GCs created on first call only
1865 coordGC = CreateOneGC(&gc_values, black, white);
1866 XSetFont(xDisplay, coordGC, coordFontID);
1868 // [HGM] make font for holdings counts (white on black)
1869 countGC = CreateOneGC(&gc_values, white, black);
1870 XSetFont(xDisplay, countGC, countFontID);
1872 lineGC = CreateOneGC(&gc_values, black, black);
1874 if (appData.monoMode) {
1876 highlineGC = CreateOneGC(&gc_values, white, white);
1877 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1878 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1880 if (DefaultDepth(xDisplay, xScreen) == 1) {
1881 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1882 gc_values.function = GXcopyInverted;
1883 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1884 gc_values.function = GXcopy;
1885 if (XBlackPixel(xDisplay, xScreen) == 1) {
1886 bwPieceGC = darkSquareGC;
1887 wbPieceGC = copyInvertedGC;
1889 bwPieceGC = copyInvertedGC;
1890 wbPieceGC = lightSquareGC;
1895 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1896 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1897 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1898 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1899 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1900 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1901 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1902 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1907 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1915 fp = fopen(filename, "rb");
1917 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1924 for (y=0; y<h; ++y) {
1925 for (x=0; x<h; ++x) {
1930 XPutPixel(xim, x, y, blackPieceColor);
1932 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1935 XPutPixel(xim, x, y, darkSquareColor);
1937 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1940 XPutPixel(xim, x, y, whitePieceColor);
1942 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1945 XPutPixel(xim, x, y, lightSquareColor);
1947 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1955 /* create Pixmap of piece */
1956 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1958 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1961 /* create Pixmap of clipmask
1962 Note: We assume the white/black pieces have the same
1963 outline, so we make only 6 masks. This is okay
1964 since the XPM clipmask routines do the same. */
1966 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1968 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1971 /* now create the 1-bit version */
1972 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1975 values.foreground = 1;
1976 values.background = 0;
1978 /* Don't use XtGetGC, not read only */
1979 maskGC = XCreateGC(xDisplay, *mask,
1980 GCForeground | GCBackground, &values);
1981 XCopyPlane(xDisplay, temp, *mask, maskGC,
1982 0, 0, squareSize, squareSize, 0, 0, 1);
1983 XFreePixmap(xDisplay, temp);
1988 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1996 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2001 /* The XSynchronize calls were copied from CreatePieces.
2002 Not sure if needed, but can't hurt */
2003 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2006 /* temp needed by loadXIM() */
2007 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2008 0, 0, ss, ss, AllPlanes, XYPixmap);
2010 if (strlen(appData.pixmapDirectory) == 0) {
2014 if (appData.monoMode) {
2015 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2019 fprintf(stderr, _("\nLoading XIMs...\n"));
2021 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2022 fprintf(stderr, "%d", piece+1);
2023 for (kind=0; kind<4; kind++) {
2024 fprintf(stderr, ".");
2025 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2026 ExpandPathName(appData.pixmapDirectory),
2027 piece <= (int) WhiteKing ? "" : "w",
2028 pieceBitmapNames[piece],
2030 ximPieceBitmap[kind][piece] =
2031 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2032 0, 0, ss, ss, AllPlanes, XYPixmap);
2033 if (appData.debugMode)
2034 fprintf(stderr, _("(File:%s:) "), buf);
2035 loadXIM(ximPieceBitmap[kind][piece],
2037 &(xpmPieceBitmap2[kind][piece]),
2038 &(ximMaskPm2[piece]));
2039 if(piece <= (int)WhiteKing)
2040 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2042 fprintf(stderr," ");
2044 /* Load light and dark squares */
2045 /* If the LSQ and DSQ pieces don't exist, we will
2046 draw them with solid squares. */
2047 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2048 if (access(buf, 0) != 0) {
2052 fprintf(stderr, _("light square "));
2054 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2055 0, 0, ss, ss, AllPlanes, XYPixmap);
2056 if (appData.debugMode)
2057 fprintf(stderr, _("(File:%s:) "), buf);
2059 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2060 fprintf(stderr, _("dark square "));
2061 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2062 ExpandPathName(appData.pixmapDirectory), ss);
2063 if (appData.debugMode)
2064 fprintf(stderr, _("(File:%s:) "), buf);
2066 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2067 0, 0, ss, ss, AllPlanes, XYPixmap);
2068 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2069 xpmJailSquare = xpmLightSquare;
2071 fprintf(stderr, _("Done.\n"));
2073 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2076 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2080 CreateXPMBoard (char *s, int kind)
2084 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2085 if(strstr(s, ".png")) {
2086 cairo_surface_t *img = cairo_image_surface_create_from_png (s);
2088 useTexture |= kind + 1; pngBoardBitmap[kind] = img;
2089 textureW[kind] = cairo_image_surface_get_width (img);
2090 textureH[kind] = cairo_image_surface_get_height (img);
2093 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2094 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2100 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2101 // thisroutine has to be called t free the old piece pixmaps
2103 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2104 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2106 XFreePixmap(xDisplay, xpmLightSquare);
2107 XFreePixmap(xDisplay, xpmDarkSquare);
2116 u_int ss = squareSize;
2118 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2119 XpmColorSymbol symbols[4];
2120 static int redo = False;
2122 if(redo) FreeXPMPieces(); else redo = 1;
2124 /* The XSynchronize calls were copied from CreatePieces.
2125 Not sure if needed, but can't hurt */
2126 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2128 /* Setup translations so piece colors match square colors */
2129 symbols[0].name = "light_piece";
2130 symbols[0].value = appData.whitePieceColor;
2131 symbols[1].name = "dark_piece";
2132 symbols[1].value = appData.blackPieceColor;
2133 symbols[2].name = "light_square";
2134 symbols[2].value = appData.lightSquareColor;
2135 symbols[3].name = "dark_square";
2136 symbols[3].value = appData.darkSquareColor;
2138 attr.valuemask = XpmColorSymbols;
2139 attr.colorsymbols = symbols;
2140 attr.numsymbols = 4;
2142 if (appData.monoMode) {
2143 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2147 if (strlen(appData.pixmapDirectory) == 0) {
2148 XpmPieces* pieces = builtInXpms;
2151 while (pieces->size != squareSize && pieces->size) pieces++;
2152 if (!pieces->size) {
2153 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2156 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2157 for (kind=0; kind<4; kind++) {
2159 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2160 pieces->xpm[piece][kind],
2161 &(xpmPieceBitmap2[kind][piece]),
2162 NULL, &attr)) != 0) {
2163 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2167 if(piece <= (int) WhiteKing)
2168 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2172 xpmJailSquare = xpmLightSquare;
2176 fprintf(stderr, _("\nLoading XPMs...\n"));
2179 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2180 fprintf(stderr, "%d ", piece+1);
2181 for (kind=0; kind<4; kind++) {
2182 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2183 ExpandPathName(appData.pixmapDirectory),
2184 piece > (int) WhiteKing ? "w" : "",
2185 pieceBitmapNames[piece],
2187 if (appData.debugMode) {
2188 fprintf(stderr, _("(File:%s:) "), buf);
2190 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2191 &(xpmPieceBitmap2[kind][piece]),
2192 NULL, &attr)) != 0) {
2193 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2194 // [HGM] missing: read of unorthodox piece failed; substitute King.
2195 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2196 ExpandPathName(appData.pixmapDirectory),
2198 if (appData.debugMode) {
2199 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2201 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2202 &(xpmPieceBitmap2[kind][piece]),
2206 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2211 if(piece <= (int) WhiteKing)
2212 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2215 /* Load light and dark squares */
2216 /* If the LSQ and DSQ pieces don't exist, we will
2217 draw them with solid squares. */
2218 fprintf(stderr, _("light square "));
2219 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2220 if (access(buf, 0) != 0) {
2224 if (appData.debugMode)
2225 fprintf(stderr, _("(File:%s:) "), buf);
2227 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2228 &xpmLightSquare, NULL, &attr)) != 0) {
2229 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2232 fprintf(stderr, _("dark square "));
2233 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2234 ExpandPathName(appData.pixmapDirectory), ss);
2235 if (appData.debugMode) {
2236 fprintf(stderr, _("(File:%s:) "), buf);
2238 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2239 &xpmDarkSquare, NULL, &attr)) != 0) {
2240 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2244 xpmJailSquare = xpmLightSquare;
2245 fprintf(stderr, _("Done.\n"));
2247 oldVariant = -1; // kludge to force re-makig of animation masks
2248 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2251 #endif /* HAVE_LIBXPM */
2253 char *pngPieceNames[] = // must be in same order as internal piece encoding
2254 { "Pawn", "Knight", "Bishop", "Rook", "Queen", "Advisor", "Elephant", "Archbishop", "Marshall", "Gold", "Commoner",
2255 "Canon", "Nightrider", "CrownedBishop", "CrownedRook", "Princess", "Chancellor", "Hawk", "Lance", "Cobra", "Unicorn", "King",
2256 "GoldKnight", "GoldLance", "GoldPawn", "GoldSilver", NULL
2260 ScaleOnePiece (char *name, int color, int piece)
2264 cairo_surface_t *img, *cs;
2266 static cairo_surface_t *pngPieceImages[2][(int)BlackPawn+4]; // png 256 x 256 images
2268 if((img = pngPieceImages[color][piece]) == NULL) { // if PNG file for this piece was not yet read, read it now and store it
2269 snprintf(buf, MSG_SIZ, "%s/%s%s.png", appData.pngDirectory, color ? "Black" : "White", pngPieceNames[piece]);
2270 pngPieceImages[color][piece] = img = cairo_image_surface_create_from_png (buf);
2271 w = cairo_image_surface_get_width (img);
2272 h = cairo_image_surface_get_height (img);
2273 if(w != 64 || h != 64) { printf("Bad png size %dx%d in %s\n", w, h, buf); exit(1); }
2275 // create new bitmap to hold scaled piece image (and remove any old)
2276 if(pngPieceBitmaps2[color][piece]) cairo_surface_destroy (pngPieceBitmaps2[color][piece]);
2277 pngPieceBitmaps2[color][piece] = cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
2278 if(piece <= WhiteKing) pngPieceBitmaps[color][piece] = cs;
2279 // scaled copying of the raw png image
2280 cr = cairo_create(cs);
2281 cairo_scale(cr, squareSize/64., squareSize/64.);
2282 cairo_set_source_surface (cr, img, 0, 0);
2292 for(p=0; pngPieceNames[p]; p++) {
2293 ScaleOnePiece(pngPieceNames[p], 0, p);
2294 ScaleOnePiece(pngPieceNames[p], 1, p);
2299 /* No built-in bitmaps */
2304 u_int ss = squareSize;
2306 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2309 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2310 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2311 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2312 pieceBitmapNames[piece],
2313 ss, kind == SOLID ? 's' : 'o');
2314 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2315 if(piece <= (int)WhiteKing)
2316 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2320 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2324 /* With built-in bitmaps */
2328 BuiltInBits* bib = builtInBits;
2331 u_int ss = squareSize;
2333 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2336 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2338 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2339 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2340 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2341 pieceBitmapNames[piece],
2342 ss, kind == SOLID ? 's' : 'o');
2343 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2344 bib->bits[kind][piece], ss, ss);
2345 if(piece <= (int)WhiteKing)
2346 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2350 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2356 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2361 char msg[MSG_SIZ], fullname[MSG_SIZ];
2363 if (*appData.bitmapDirectory != NULLCHAR) {
2364 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2365 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2366 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2367 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2368 &w, &h, pm, &x_hot, &y_hot);
2369 fprintf(stderr, "load %s\n", name);
2370 if (errcode != BitmapSuccess) {
2372 case BitmapOpenFailed:
2373 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2375 case BitmapFileInvalid:
2376 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2378 case BitmapNoMemory:
2379 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2383 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2387 fprintf(stderr, _("%s: %s...using built-in\n"),
2389 } else if (w != wreq || h != hreq) {
2391 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2392 programName, fullname, w, h, wreq, hreq);
2398 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2408 if (lineGap == 0) return;
2410 /* [HR] Split this into 2 loops for non-square boards. */
2412 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2413 gridSegments[i].x1 = 0;
2414 gridSegments[i].x2 =
2415 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2416 gridSegments[i].y1 = gridSegments[i].y2
2417 = lineGap / 2 + (i * (squareSize + lineGap));
2420 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2421 gridSegments[j + i].y1 = 0;
2422 gridSegments[j + i].y2 =
2423 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2424 gridSegments[j + i].x1 = gridSegments[j + i].x2
2425 = lineGap / 2 + (j * (squareSize + lineGap));
2430 MarkMenuItem (char *menuRef, int state)
2432 MenuItem *item = MenuNameToItem(menuRef);
2436 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2437 XtSetValues(item->handle, args, 1);
2442 EnableNamedMenuItem (char *menuRef, int state)
2444 MenuItem *item = MenuNameToItem(menuRef);
2446 if(item) XtSetSensitive(item->handle, state);
2450 EnableButtonBar (int state)
2452 XtSetSensitive(optList[W_BUTTON].handle, state);
2457 SetMenuEnables (Enables *enab)
2459 while (enab->name != NULL) {
2460 EnableNamedMenuItem(enab->name, enab->value);
2466 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2467 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2469 if(*nprms == 0) return;
2470 item = MenuNameToItem(prms[0]);
2471 if(item) ((MenuProc *) item->proc) ();
2475 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2477 RecentEngineEvent((int) (intptr_t) addr);
2481 AppendMenuItem (char *msg, int n)
2483 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2495 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2496 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2497 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2498 dmEnables[i].piece);
2499 XtSetSensitive(entry, p != NULL || !appData.testLegality
2500 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2501 && !appData.icsActive));
2503 while (p && *p++ == dmEnables[i].piece) count++;
2504 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2506 XtSetArg(args[j], XtNlabel, label); j++;
2507 XtSetValues(entry, args, j);
2513 do_flash_delay (unsigned long msec)
2519 DoDrawBorder (cairo_surface_t *cs, int x, int y, int type)
2524 cr = cairo_create(cs);
2525 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
2526 cairo_rectangle(cr, x, y, squareSize+lineGap, squareSize+lineGap);
2527 SetPen(cr, lineGap, type == 1 ? appData.highlightSquareColor : appData.premoveHighlightColor, 0);
2532 DrawBorder (int x, int y, int type)
2534 DoDrawBorder(csBoardWindow, x, y, type);
2535 DoDrawBorder(csBoardBackup, x, y, type);
2539 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2541 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2542 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2544 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2545 if(textureW[kind] < W*squareSize)
2546 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2548 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2549 if(textureH[kind] < H*squareSize)
2550 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2552 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2557 DrawLogo (void *handle, void *logo)
2559 cairo_surface_t *img, *cs;
2563 if(!logo || !handle) return;
2564 cs = cairo_xlib_surface_create(xDisplay, XtWindow(handle), DefaultVisual(xDisplay, 0), appData.logoSize, appData.logoSize/2);
2565 img = cairo_image_surface_create_from_png (logo);
2566 w = cairo_image_surface_get_width (img);
2567 h = cairo_image_surface_get_height (img);
2568 cr = cairo_create(cs);
2569 cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
2570 cairo_set_source_surface (cr, img, 0, 0);
2573 cairo_surface_destroy (img);
2574 cairo_surface_destroy (cs);
2578 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2579 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2581 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2582 if(pngBoardBitmap[color]) {
2584 if(!fac && !cairoAnimate) return;
2586 cr = cairo_create (fac ? csBoardWindow : (cairo_surface_t *) dest);
2587 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2588 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2589 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2593 cr = cairo_create (csBoardBackup);
2594 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2595 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2596 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2601 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2602 squareSize, squareSize, x*fac, y*fac);
2605 cairo_t *cr = cairo_create (csBoardWindow);
2608 case 0: col = appData.darkSquareColor; break;
2609 case 1: col = appData.lightSquareColor; break;
2610 case 2: col = "#000000"; break;
2612 SetPen(cr, 2.0, col, 0);
2613 cairo_rectangle (cr, x, y, squareSize, squareSize);
2616 cr = cairo_create (csBoardBackup);
2617 SetPen(cr, 2.0, col, 0);
2618 cairo_rectangle (cr, x, y, squareSize, squareSize);
2622 if (useImages && useImageSqs) {
2626 pm = xpmLightSquare;
2631 case 2: /* neutral */
2633 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2636 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2637 squareSize, squareSize, x*fac, y*fac);
2647 case 2: /* neutral */
2652 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2657 I split out the routines to draw a piece so that I could
2658 make a generic flash routine.
2661 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2663 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2664 switch (square_color) {
2666 case 2: /* neutral */
2668 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2669 ? *pieceToOutline(piece)
2670 : *pieceToSolid(piece),
2671 dest, bwPieceGC, 0, 0,
2672 squareSize, squareSize, x, y);
2675 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2676 ? *pieceToSolid(piece)
2677 : *pieceToOutline(piece),
2678 dest, wbPieceGC, 0, 0,
2679 squareSize, squareSize, x, y);
2685 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2687 switch (square_color) {
2689 case 2: /* neutral */
2691 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2692 ? *pieceToOutline(piece)
2693 : *pieceToSolid(piece),
2694 dest, bwPieceGC, 0, 0,
2695 squareSize, squareSize, x, y, 1);
2698 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2699 ? *pieceToSolid(piece)
2700 : *pieceToOutline(piece),
2701 dest, wbPieceGC, 0, 0,
2702 squareSize, squareSize, x, y, 1);
2708 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2710 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2711 switch (square_color) {
2713 XCopyPlane(xDisplay, *pieceToSolid(piece),
2714 dest, (int) piece < (int) BlackPawn
2715 ? wlPieceGC : blPieceGC, 0, 0,
2716 squareSize, squareSize, x, y, 1);
2719 XCopyPlane(xDisplay, *pieceToSolid(piece),
2720 dest, (int) piece < (int) BlackPawn
2721 ? wdPieceGC : bdPieceGC, 0, 0,
2722 squareSize, squareSize, x, y, 1);
2724 case 2: /* neutral */
2726 break; // should never contain pieces
2731 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2733 int kind, p = piece;
2735 switch (square_color) {
2737 case 2: /* neutral */
2739 if ((int)piece < (int) BlackPawn) {
2747 if ((int)piece < (int) BlackPawn) {
2755 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2756 if(useTexture & square_color+1) {
2757 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2758 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2759 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2760 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2761 XSetClipMask(xDisplay, wlPieceGC, None);
2762 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2764 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2765 dest, wlPieceGC, 0, 0,
2766 squareSize, squareSize, x, y);
2770 pngDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2772 int kind, p = piece;
2775 if ((int)piece < (int) BlackPawn) {
2781 if(appData.upsideDown && flipView) { p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2782 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2784 cr = cairo_create (csBoardWindow);
2785 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2788 cr = cairo_create (csBoardBackup);
2789 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2794 typedef void (*DrawFunc)();
2799 if (appData.monoMode) {
2800 if (DefaultDepth(xDisplay, xScreen) == 1) {
2801 return monoDrawPiece_1bit;
2803 return monoDrawPiece;
2805 } else if(appData.pngDirectory[0]) {
2806 return pngDrawPiece;
2809 return colorDrawPieceImage;
2811 return colorDrawPiece;
2816 DoDrawDot (int marker, int x, int y, int r, cairo_surface_t *cs)
2820 cr = cairo_create(cs);
2821 cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
2822 if(appData.monoMode) {
2823 SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
2824 cairo_stroke_preserve(cr);
2825 SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
2827 SetPen(cr, 2, marker == 2 ? "#FF0000" : "#FFFF00", 0);
2836 DrawDot (int marker, int x, int y, int r)
2838 DoDrawDot(marker, x, y, r, csBoardWindow);
2839 DoDrawDot(marker, x, y, r, csBoardBackup);
2843 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2844 { // basic front-end board-draw function: takes care of everything that can be in square:
2845 // piece, background, coordinate/count, marker dot
2846 int direction, font_ascent, font_descent;
2847 XCharStruct overall;
2850 if (piece == EmptySquare) {
2851 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2853 drawfunc = ChooseDrawFunc();
2854 drawfunc(piece, square_color, x, y, xBoardWindow);
2857 if(align) { // square carries inscription (coord or piece count)
2859 GC hGC = align < 3 ? coordGC : countGC;
2860 // first calculate where it goes
2861 XTextExtents(countFontStruct, string, 1, &direction,
2862 &font_ascent, &font_descent, &overall);
2864 xx += squareSize - overall.width - 2;
2865 yy += squareSize - font_descent - 1;
2866 } else if (align == 2) {
2867 xx += 2, yy += font_ascent + 1;
2868 } else if (align == 3) {
2869 xx += squareSize - overall.width - 2;
2870 yy += font_ascent + 1;
2871 } else if (align == 4) {
2872 xx += 2, yy += font_ascent + 1;
2875 if (appData.monoMode) {
2876 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2878 if(*appData.pngDirectory) {
2879 cairo_t *cr = cairo_create (csBoardWindow);
2880 cairo_select_font_face (cr, "Sans",
2881 CAIRO_FONT_SLANT_NORMAL,
2882 CAIRO_FONT_WEIGHT_BOLD);
2884 cairo_set_font_size (cr, squareSize/4);
2886 cairo_move_to (cr, xx-1, yy);
2887 if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2888 else cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
2889 cairo_show_text (cr, string);
2891 cr = cairo_create (csBoardBackup);
2892 cairo_select_font_face (cr, "Sans",
2893 CAIRO_FONT_SLANT_NORMAL,
2894 CAIRO_FONT_WEIGHT_BOLD);
2896 cairo_set_font_size (cr, squareSize/4);
2898 cairo_move_to (cr, xx-1, yy);
2899 if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2900 else cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
2901 cairo_show_text (cr, string);
2904 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2908 if(marker) { // print fat marker dot, if requested
2909 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2914 FlashDelay (int flash_delay)
2916 XSync(xDisplay, False);
2917 if(flash_delay) do_flash_delay(flash_delay);
2921 Fraction (int x, int start, int stop)
2923 double f = ((double) x - start)/(stop - start);
2924 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2928 static WindowPlacement wpNew;
2931 CoDrag (Widget sh, WindowPlacement *wp)
2934 int j=0, touch=0, fudge = 2;
2935 GetActualPlacement(sh, wp);
2936 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2937 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2938 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2939 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2940 if(!touch ) return; // only windows that touch co-move
2941 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2942 int heightInc = wpNew.height - wpMain.height;
2943 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2944 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2945 wp->y += fracTop * heightInc;
2946 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2947 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2948 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2949 int widthInc = wpNew.width - wpMain.width;
2950 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2951 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2952 wp->y += fracLeft * widthInc;
2953 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2954 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2956 wp->x += wpNew.x - wpMain.x;
2957 wp->y += wpNew.y - wpMain.y;
2958 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2959 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2960 XtSetArg(args[j], XtNx, wp->x); j++;
2961 XtSetArg(args[j], XtNy, wp->y); j++;
2962 XtSetValues(sh, args, j);
2966 ReSize (WindowPlacement *wp)
2969 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
2970 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
2971 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
2972 if(sqy < sqx) sqx = sqy;
2973 if(sqx != squareSize) {
2974 squareSize = sqx; // adopt new square size
2976 CreatePNGPieces(); // make newly scaled pieces
2977 InitDrawingSizes(0, 0); // creates grid etc.
2981 static XtIntervalId delayedDragID = 0;
2990 GetActualPlacement(shellWidget, &wpNew);
2991 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2992 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
2993 busy = 0; return; // false alarm
2996 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2997 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2998 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2999 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
3001 DrawPosition(True, NULL);
3002 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3010 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3012 XtAppAddTimeOut(appContext, 100, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3016 EventProc (Widget widget, caddr_t unused, XEvent *event)
3018 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
3019 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3022 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
3025 Color (char *col, int n)
3028 sscanf(col, "#%x", &c);
3034 SetPen (cairo_t *cr, float w, char *col, int dash)
3036 static const double dotted[] = {4.0, 4.0};
3037 static int len = sizeof(dotted) / sizeof(dotted[0]);
3038 cairo_set_line_width (cr, w);
3039 cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
3040 if(dash) cairo_set_dash (cr, dotted, len, 0.0);
3043 void DrawSeekAxis( int x, int y, int xTo, int yTo )
3048 cr = cairo_create (csBoardWindow);
3050 cairo_move_to (cr, x, y);
3051 cairo_line_to(cr, xTo, yTo );
3053 SetPen(cr, 2, "#000000", 0);
3060 void DrawSeekBackground( int left, int top, int right, int bottom )
3062 cairo_t *cr = cairo_create (csBoardWindow);
3064 cairo_rectangle (cr, left, top, right-left, bottom-top);
3066 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
3073 void DrawSeekText(char *buf, int x, int y)
3075 cairo_t *cr = cairo_create (csBoardWindow);
3077 cairo_select_font_face (cr, "Sans",
3078 CAIRO_FONT_SLANT_NORMAL,
3079 CAIRO_FONT_WEIGHT_NORMAL);
3081 cairo_set_font_size (cr, 12.0);
3083 cairo_move_to (cr, x, y+4);
3084 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
3085 cairo_show_text( cr, buf);
3091 void DrawSeekDot(int x, int y, int colorNr)
3093 cairo_t *cr = cairo_create (csBoardWindow);
3094 int square = colorNr & 0x80;
3098 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3100 cairo_arc(cr, x, y, squareSize/8, 0.0, 2*M_PI);
3102 SetPen(cr, 2, "#000000", 0);
3103 cairo_stroke_preserve(cr);
3105 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
3106 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
3107 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
3118 int boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3119 int boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3120 if(!csBoardWindow) {
3121 csBoardWindow = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), boardWidth, boardHeight);
3122 csBoardBackup = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, boardWidth, boardHeight);
3132 DoDrawGrid(cairo_surface_t *cs)
3134 /* draws a grid starting around Nx, Ny squares starting at x,y */
3140 cr = cairo_create (cs);
3142 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
3143 SetPen(cr, lineGap, "#000000", 0);
3146 for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
3148 cairo_move_to (cr, gridSegments[i].x1, gridSegments[i].y1);
3149 cairo_line_to (cr, gridSegments[i].x2, gridSegments[i].y2);
3162 DoDrawGrid(csBoardWindow);
3163 DoDrawGrid(csBoardBackup);
3167 * event handler for redrawing the board
3170 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3172 DrawPosition(True, NULL);
3177 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3178 { // [HGM] pv: walk PV
3179 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3182 static int savedIndex; /* gross that this is global */
3185 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3188 XawTextPosition index, dummy;
3191 XawTextGetSelectionPos(w, &index, &dummy);
3192 XtSetArg(arg, XtNstring, &val);
3193 XtGetValues(w, &arg, 1);
3194 ReplaceComment(savedIndex, val);
3195 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3196 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3200 EditCommentPopUp (int index, char *title, char *text)
3203 if (text == NULL) text = "";
3204 NewCommentPopup(title, text, index);
3208 CommentPopUp (char *title, char *text)
3210 savedIndex = currentMove; // [HGM] vari
3211 NewCommentPopup(title, text, currentMove);
3217 PopDown(CommentDlg);
3221 /* Disable all user input other than deleting the window */
3222 static int frozen = 0;
3228 /* Grab by a widget that doesn't accept input */
3229 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
3233 /* Undo a FreezeUI */
3237 if (!frozen) return;
3238 XtRemoveGrab(optList[W_MESSG].handle);
3246 static int oldPausing = FALSE;
3247 static GameMode oldmode = (GameMode) -1;
3250 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3252 if (pausing != oldPausing) {
3253 oldPausing = pausing;
3254 MarkMenuItem("Mode.Pause", pausing);
3256 if (appData.showButtonBar) {
3257 /* Always toggle, don't set. Previous code messes up when
3258 invoked while the button is pressed, as releasing it
3259 toggles the state again. */
3262 XtSetArg(args[0], XtNbackground, &oldbg);
3263 XtSetArg(args[1], XtNforeground, &oldfg);
3264 XtGetValues(optList[W_PAUSE].handle,
3266 XtSetArg(args[0], XtNbackground, oldfg);
3267 XtSetArg(args[1], XtNforeground, oldbg);
3269 XtSetValues(optList[W_PAUSE].handle, args, 2);
3273 wname = ModeToWidgetName(oldmode);
3274 if (wname != NULL) {
3275 MarkMenuItem(wname, False);
3277 wname = ModeToWidgetName(gameMode);
3278 if (wname != NULL) {
3279 MarkMenuItem(wname, True);
3282 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
3284 /* Maybe all the enables should be handled here, not just this one */
3285 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
3287 DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
3292 * Button/menu procedures
3295 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3296 char *selected_fen_position=NULL;
3299 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3300 Atom *type_return, XtPointer *value_return,
3301 unsigned long *length_return, int *format_return)
3303 char *selection_tmp;
3305 // if (!selected_fen_position) return False; /* should never happen */
3306 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3307 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3308 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3311 if (f == NULL) return False;
3315 selection_tmp = XtMalloc(len + 1);
3316 count = fread(selection_tmp, 1, len, f);
3319 XtFree(selection_tmp);
3322 selection_tmp[len] = NULLCHAR;
3324 /* note: since no XtSelectionDoneProc was registered, Xt will
3325 * automatically call XtFree on the value returned. So have to
3326 * make a copy of it allocated with XtMalloc */
3327 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3328 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3331 *value_return=selection_tmp;
3332 *length_return=strlen(selection_tmp);
3333 *type_return=*target;
3334 *format_return = 8; /* bits per byte */
3336 } else if (*target == XA_TARGETS(xDisplay)) {
3337 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3338 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3339 targets_tmp[1] = XA_STRING;
3340 *value_return = targets_tmp;
3341 *type_return = XA_ATOM;
3344 // This code leads to a read of value_return out of bounds on 64-bit systems.
3345 // Other code which I have seen always sets *format_return to 32 independent of
3346 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3347 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3348 *format_return = 8 * sizeof(Atom);
3349 if (*format_return > 32) {
3350 *length_return *= *format_return / 32;
3351 *format_return = 32;
3354 *format_return = 32;
3362 /* note: when called from menu all parameters are NULL, so no clue what the
3363 * Widget which was clicked on was, or what the click event was
3366 CopySomething (char *src)
3368 selected_fen_position = src;
3370 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3371 * have a notion of a position that is selected but not copied.
3372 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3374 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3376 SendPositionSelection,
3377 NULL/* lose_ownership_proc */ ,
3378 NULL/* transfer_done_proc */);
3379 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3381 SendPositionSelection,
3382 NULL/* lose_ownership_proc */ ,
3383 NULL/* transfer_done_proc */);
3386 /* function called when the data to Paste is ready */
3388 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3389 Atom *type, XtPointer value, unsigned long *len, int *format)
3392 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3393 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3394 EditPositionPasteFEN(fenstr);
3398 /* called when Paste Position button is pressed,
3399 * all parameters will be NULL */
3401 PastePositionProc ()
3403 XtGetSelectionValue(menuBarWidget,
3404 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3405 /* (XtSelectionCallbackProc) */ PastePositionCB,
3406 NULL, /* client_data passed to PastePositionCB */
3408 /* better to use the time field from the event that triggered the
3409 * call to this function, but that isn't trivial to get
3416 /* note: when called from menu all parameters are NULL, so no clue what the
3417 * Widget which was clicked on was, or what the click event was
3419 /* function called when the data to Paste is ready */
3421 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3422 Atom *type, XtPointer value, unsigned long *len, int *format)
3425 if (value == NULL || *len == 0) {
3426 return; /* nothing had been selected to copy */
3428 f = fopen(gamePasteFilename, "w");
3430 DisplayError(_("Can't open temp file"), errno);
3433 fwrite(value, 1, *len, f);
3436 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3439 /* called when Paste Game button is pressed,
3440 * all parameters will be NULL */
3444 XtGetSelectionValue(menuBarWidget,
3445 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3446 /* (XtSelectionCallbackProc) */ PasteGameCB,
3447 NULL, /* client_data passed to PasteGameCB */
3449 /* better to use the time field from the event that triggered the
3450 * call to this function, but that isn't trivial to get
3459 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3466 { // bassic primitive for determining if modifier keys are pressed
3467 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3470 XQueryKeymap(xDisplay,keys);
3471 for(i=0; i<6; i++) {
3473 j = XKeysymToKeycode(xDisplay, codes[i]);
3474 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3480 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3484 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3485 if ( n == 1 && *buf >= 32 // printable
3486 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3487 ) BoxAutoPopUp (buf);
3491 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3492 { // [HGM] input: let up-arrow recall previous line from history
3497 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3498 { // [HGM] input: let down-arrow recall next line from history
3503 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3509 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3511 if (!TempBackwardActive) {
3512 TempBackwardActive = True;
3518 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3520 /* Check to see if triggered by a key release event for a repeating key.
3521 * If so the next queued event will be a key press of the same key at the same time */
3522 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3524 XPeekEvent(xDisplay, &next);
3525 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3526 next.xkey.keycode == event->xkey.keycode)
3530 TempBackwardActive = False;
3534 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3535 { // called as key binding
3538 if (nprms && *nprms > 0)
3542 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3548 { // called from menu
3549 ManInner(NULL, NULL, NULL, NULL);
3553 SetWindowTitle (char *text, char *title, char *icon)
3557 if (appData.titleInWindow) {
3559 XtSetArg(args[i], XtNlabel, text); i++;
3560 XtSetValues(titleWidget, args, i);
3563 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3564 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3565 XtSetValues(shellWidget, args, i);
3566 XSync(xDisplay, False);
3571 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3577 DisplayIcsInteractionTitle (String message)
3579 if (oldICSInteractionTitle == NULL) {
3580 /* Magic to find the old window title, adapted from vim */
3581 char *wina = getenv("WINDOWID");
3583 Window win = (Window) atoi(wina);
3584 Window root, parent, *children;
3585 unsigned int nchildren;
3586 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3588 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3589 if (!XQueryTree(xDisplay, win, &root, &parent,
3590 &children, &nchildren)) break;
3591 if (children) XFree((void *)children);
3592 if (parent == root || parent == 0) break;
3595 XSetErrorHandler(oldHandler);
3597 if (oldICSInteractionTitle == NULL) {
3598 oldICSInteractionTitle = "xterm";
3601 printf("\033]0;%s\007", message);
3606 XtIntervalId delayedEventTimerXID = 0;
3607 DelayedEventCallback delayedEventCallback = 0;
3612 delayedEventTimerXID = 0;
3613 delayedEventCallback();
3617 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3619 if(delayedEventTimerXID && delayedEventCallback == cb)
3620 // [HGM] alive: replace, rather than add or flush identical event
3621 XtRemoveTimeOut(delayedEventTimerXID);
3622 delayedEventCallback = cb;
3623 delayedEventTimerXID =
3624 XtAppAddTimeOut(appContext, millisec,
3625 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3628 DelayedEventCallback
3631 if (delayedEventTimerXID) {
3632 return delayedEventCallback;
3639 CancelDelayedEvent ()
3641 if (delayedEventTimerXID) {
3642 XtRemoveTimeOut(delayedEventTimerXID);
3643 delayedEventTimerXID = 0;
3647 XtIntervalId loadGameTimerXID = 0;
3650 LoadGameTimerRunning ()
3652 return loadGameTimerXID != 0;
3656 StopLoadGameTimer ()
3658 if (loadGameTimerXID != 0) {
3659 XtRemoveTimeOut(loadGameTimerXID);
3660 loadGameTimerXID = 0;
3668 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3670 loadGameTimerXID = 0;
3675 StartLoadGameTimer (long millisec)
3678 XtAppAddTimeOut(appContext, millisec,
3679 (XtTimerCallbackProc) LoadGameTimerCallback,
3683 XtIntervalId analysisClockXID = 0;
3686 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3688 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3689 || appData.icsEngineAnalyze) { // [DM]
3690 AnalysisPeriodicEvent(0);
3691 StartAnalysisClock();
3696 StartAnalysisClock ()
3699 XtAppAddTimeOut(appContext, 2000,
3700 (XtTimerCallbackProc) AnalysisClockCallback,
3704 XtIntervalId clockTimerXID = 0;
3707 ClockTimerRunning ()
3709 return clockTimerXID != 0;
3715 if (clockTimerXID != 0) {
3716 XtRemoveTimeOut(clockTimerXID);
3725 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3732 StartClockTimer (long millisec)
3735 XtAppAddTimeOut(appContext, millisec,
3736 (XtTimerCallbackProc) ClockTimerCallback,
3741 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3745 Widget w = (Widget) opt->handle;
3747 /* check for low time warning */
3748 Pixel foregroundOrWarningColor = timerForegroundPixel;
3751 appData.lowTimeWarning &&
3752 (timer / 1000) < appData.icsAlarmTime)
3753 foregroundOrWarningColor = lowTimeWarningColor;
3755 if (appData.clockMode) {
3756 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
3757 XtSetArg(args[0], XtNlabel, buf);
3759 snprintf(buf, MSG_SIZ, "%s ", color);
3760 XtSetArg(args[0], XtNlabel, buf);
3765 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3766 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3768 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3769 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3772 XtSetValues(w, args, 3);
3775 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3778 SetClockIcon (int color)
3781 Pixmap pm = *clockIcons[color];
3782 if (iconPixmap != pm) {
3784 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3785 XtSetValues(shellWidget, args, 1);
3790 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3792 InputSource *is = (InputSource *) closure;
3797 if (is->lineByLine) {
3798 count = read(is->fd, is->unused,
3799 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3801 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3804 is->unused += count;
3806 while (p < is->unused) {
3807 q = memchr(p, '\n', is->unused - p);
3808 if (q == NULL) break;
3810 (is->func)(is, is->closure, p, q - p, 0);
3814 while (p < is->unused) {
3819 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3824 (is->func)(is, is->closure, is->buf, count, error);
3829 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3832 ChildProc *cp = (ChildProc *) pr;
3834 is = (InputSource *) calloc(1, sizeof(InputSource));
3835 is->lineByLine = lineByLine;
3839 is->fd = fileno(stdin);
3841 is->kind = cp->kind;
3842 is->fd = cp->fdFrom;
3845 is->unused = is->buf;
3848 is->xid = XtAppAddInput(appContext, is->fd,
3849 (XtPointer) (XtInputReadMask),
3850 (XtInputCallbackProc) DoInputCallback,
3852 is->closure = closure;
3853 return (InputSourceRef) is;
3857 RemoveInputSource (InputSourceRef isr)
3859 InputSource *is = (InputSource *) isr;
3861 if (is->xid == 0) return;
3862 XtRemoveInput(is->xid);
3866 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3868 /* Masks for XPM pieces. Black and white pieces can have
3869 different shapes, but in the interest of retaining my
3870 sanity pieces must have the same outline on both light
3871 and dark squares, and all pieces must use the same
3872 background square colors/images. */
3874 static int xpmDone = 0;
3875 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3876 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3877 static cairo_surface_t *c_animBufs[3*NrOfAnims]; // newBuf, saveBuf
3880 CreateAnimMasks (int pieceDepth)
3886 unsigned long plane;
3889 /* Need a bitmap just to get a GC with right depth */
3890 buf = XCreatePixmap(xDisplay, xBoardWindow,
3892 values.foreground = 1;
3893 values.background = 0;
3894 /* Don't use XtGetGC, not read only */
3895 maskGC = XCreateGC(xDisplay, buf,
3896 GCForeground | GCBackground, &values);
3897 XFreePixmap(xDisplay, buf);
3899 buf = XCreatePixmap(xDisplay, xBoardWindow,
3900 squareSize, squareSize, pieceDepth);
3901 values.foreground = XBlackPixel(xDisplay, xScreen);
3902 values.background = XWhitePixel(xDisplay, xScreen);
3903 bufGC = XCreateGC(xDisplay, buf,
3904 GCForeground | GCBackground, &values);
3906 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3907 /* Begin with empty mask */
3908 if(!xpmDone) // [HGM] pieces: keep using existing
3909 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3910 squareSize, squareSize, 1);
3911 XSetFunction(xDisplay, maskGC, GXclear);
3912 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3913 0, 0, squareSize, squareSize);
3915 /* Take a copy of the piece */
3920 XSetFunction(xDisplay, bufGC, GXcopy);
3921 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3923 0, 0, squareSize, squareSize, 0, 0);
3925 /* XOR the background (light) over the piece */
3926 XSetFunction(xDisplay, bufGC, GXxor);
3928 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3929 0, 0, squareSize, squareSize, 0, 0);
3931 XSetForeground(xDisplay, bufGC, lightSquareColor);
3932 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3935 /* We now have an inverted piece image with the background
3936 erased. Construct mask by just selecting all the non-zero
3937 pixels - no need to reconstruct the original image. */
3938 XSetFunction(xDisplay, maskGC, GXor);
3940 /* Might be quicker to download an XImage and create bitmap
3941 data from it rather than this N copies per piece, but it
3942 only takes a fraction of a second and there is a much
3943 longer delay for loading the pieces. */
3944 for (n = 0; n < pieceDepth; n ++) {
3945 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3946 0, 0, squareSize, squareSize,
3952 XFreePixmap(xDisplay, buf);
3953 XFreeGC(xDisplay, bufGC);
3954 XFreeGC(xDisplay, maskGC);
3958 InitAnimState (AnimNr anr, XWindowAttributes *info)
3964 DrawSeekOpen(); // set cs to board widget
3965 if(c_animBufs[anr]) cairo_surface_destroy (c_animBufs[anr]);
3966 if(c_animBufs[anr+2]) cairo_surface_destroy (c_animBufs[anr+2]);
3967 c_animBufs[anr+4] = csBoardWindow;
3968 c_animBufs[anr+2] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3969 c_animBufs[anr] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3972 /* Each buffer is square size, same depth as window */
3973 animBufs[anr+4] = xBoardWindow;
3974 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3975 squareSize, squareSize, info->depth);
3976 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3977 squareSize, squareSize, info->depth);
3979 /* Create a plain GC for blitting */
3980 mask = GCForeground | GCBackground | GCFunction |
3981 GCPlaneMask | GCGraphicsExposures;
3982 values.foreground = XBlackPixel(xDisplay, xScreen);
3983 values.background = XWhitePixel(xDisplay, xScreen);
3984 values.function = GXcopy;
3985 values.plane_mask = AllPlanes;
3986 values.graphics_exposures = False;
3987 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3989 /* Piece will be copied from an existing context at
3990 the start of each new animation/drag. */
3991 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3993 /* Outline will be a read-only copy of an existing */
3994 animGCs[anr+4] = None;
4000 XWindowAttributes info;
4002 if (!cairoAnimate && xpmDone && gameInfo.variant == oldVariant) return;
4003 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
4004 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
4006 InitAnimState(Game, &info);
4007 InitAnimState(Player, &info);
4009 /* For XPM pieces, we need bitmaps to use as masks. */
4010 if (useImages & !xpmDone)
4011 CreateAnimMasks(info.depth), xpmDone = 1;
4016 static Boolean frameWaiting;
4019 FrameAlarm (int sig)
4021 frameWaiting = False;
4022 /* In case System-V style signals. Needed?? */
4023 signal(SIGALRM, FrameAlarm);
4027 FrameDelay (int time)
4029 struct itimerval delay;
4031 XSync(xDisplay, False);
4034 frameWaiting = True;
4035 signal(SIGALRM, FrameAlarm);
4036 delay.it_interval.tv_sec =
4037 delay.it_value.tv_sec = time / 1000;
4038 delay.it_interval.tv_usec =
4039 delay.it_value.tv_usec = (time % 1000) * 1000;
4040 setitimer(ITIMER_REAL, &delay, NULL);
4041 while (frameWaiting) pause();
4042 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
4043 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
4044 setitimer(ITIMER_REAL, &delay, NULL);
4051 FrameDelay (int time)
4053 XSync(xDisplay, False);
4055 usleep(time * 1000);
4061 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
4065 /* Bitmap for piece being moved. */
4066 if (appData.monoMode) {
4067 *mask = *pieceToSolid(piece);
4068 } else if (useImages) {
4070 *mask = xpmMask[piece];
4072 *mask = ximMaskPm[piece];
4075 *mask = *pieceToSolid(piece);
4078 /* GC for piece being moved. Square color doesn't matter, but
4079 since it gets modified we make a copy of the original. */
4081 if (appData.monoMode)
4086 if (appData.monoMode)
4091 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
4093 /* Outline only used in mono mode and is not modified */
4095 *outline = bwPieceGC;
4097 *outline = wbPieceGC;
4101 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
4106 /* Draw solid rectangle which will be clipped to shape of piece */
4107 XFillRectangle(xDisplay, dest, clip,
4108 0, 0, squareSize, squareSize);
4109 if (appData.monoMode)
4110 /* Also draw outline in contrasting color for black
4111 on black / white on white cases */
4112 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
4113 0, 0, squareSize, squareSize, 0, 0, 1);
4115 /* Copy the piece */
4120 if(appData.upsideDown && flipView) kind ^= 2;
4121 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4123 0, 0, squareSize, squareSize,
4129 CairoOverlayPiece (ChessSquare piece, cairo_surface_t *dest)
4131 static ChessSquare oldPiece = -1;
4133 static cairo_t *pieceSource;
4134 extern int doubleClick; // in backend.c
4135 if(piece != oldPiece || squareSize != oldSize) { // try make it faster by only changing cr if we need other piece
4136 if(pieceSource) cairo_destroy (pieceSource);
4137 pieceSource = cairo_create (dest);
4138 cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
4139 oldPiece = piece; oldSize = squareSize;
4141 if(doubleClick) cairo_paint_with_alpha (pieceSource, 0.6);
4142 else cairo_paint(pieceSource);
4146 InsertPiece (AnimNr anr, ChessSquare piece)
4149 CairoOverlayPiece(piece, c_animBufs[anr]);
4151 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
4155 DrawBlank (AnimNr anr, int x, int y, int startColor)
4158 BlankSquare(x, y, startColor, EmptySquare, (Drawable) c_animBufs[anr+2], 0);
4160 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
4163 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
4164 int srcX, int srcY, int width, int height, int destX, int destY)
4167 cairo_t *cr;// = cairo_create (c_animBufs[anr+destBuf]);
4168 cr = cairo_create (c_animBufs[anr+destBuf]);
4169 if(c_animBufs[anr+srcBuf] == csBoardWindow)
4170 cairo_set_source_surface (cr, csBoardBackup, destX - srcX, destY - srcY);
4172 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
4173 cairo_rectangle (cr, destX, destY, width, height);
4176 if(c_animBufs[anr+destBuf] == csBoardWindow) {
4177 cr = cairo_create (csBoardBackup); // also draw to backup
4178 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
4179 cairo_rectangle (cr, destX, destY, width, height);
4184 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
4185 srcX, srcY, width, height, destX, destY);
4189 SetDragPiece (AnimNr anr, ChessSquare piece)
4192 if(cairoAnimate) return;
4193 /* The piece will be drawn using its own bitmap as a matte */
4194 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
4195 XSetClipMask(xDisplay, animGCs[anr+2], mask);
4198 /* [AS] Arrow highlighting support */
4200 void DrawPolygon(Pnt arrow[], int nr)
4201 { // for now on own surface; eventually this should become a global that is only destroyed on resize
4202 cairo_surface_t *boardSurface;
4205 int w = lineGap + BOARD_WIDTH * (squareSize + lineGap);
4206 int h = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
4207 boardSurface = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), w, h);
4208 cr = cairo_create (boardSurface);
4209 cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
4210 for (i=0;i<nr;i++) {
4211 cairo_line_to(cr, arrow[i].x, arrow[i].y);
4213 if(appData.monoMode) { // should we always outline arrow?
4214 cairo_line_to(cr, arrow[0].x, arrow[0].y);
4215 SetPen(cr, 2, "#000000", 0);
4216 cairo_stroke_preserve(cr);
4218 SetPen(cr, 2, appData.highlightSquareColor, 0);
4223 cairo_surface_destroy (boardSurface);
4227 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
4229 char buf[MSG_SIZ], *logoName = buf;
4230 if(appData.logo[n][0]) {
4231 logoName = appData.logo[n];
4232 } else if(appData.autoLogo) {
4233 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
4234 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
4235 } else if(appData.directory[n] && appData.directory[n][0]) {
4236 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
4240 { ASSIGN(cps->programLogo, logoName); }
4244 UpdateLogos (int displ)
4246 if(optList[W_WHITE-1].handle == NULL) return;
4247 LoadLogo(&first, 0, 0);
4248 LoadLogo(&second, 1, appData.icsActive);
4249 if(displ) DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);