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 printf("Copy pieces\n");
1059 for(p=0; p<=(int)WhiteKing; p++)
1060 pngPieceBitmaps[i][p] = pngPieceBitmaps2[i][p]; // defaults
1062 oldMono = -10; // kludge to force recreation of animation masks
1063 oldVariant = gameInfo.variant;
1066 if(appData.monoMode != oldMono || cairoAnimate)
1069 oldMono = appData.monoMode;
1073 MakeOneColor (char *name, Pixel *color)
1075 XrmValue vFrom, vTo;
1076 if (!appData.monoMode) {
1077 vFrom.addr = (caddr_t) name;
1078 vFrom.size = strlen(name);
1079 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1080 if (vTo.addr == NULL) {
1081 appData.monoMode = True;
1084 *color = *(Pixel *) vTo.addr;
1092 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1093 int forceMono = False;
1095 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1096 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1097 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1098 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1099 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1100 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1101 if (appData.lowTimeWarning)
1102 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1103 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1104 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1111 { // [HGM] taken out of main
1113 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1114 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1115 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1117 if (appData.bitmapDirectory[0] != NULLCHAR) {
1121 CreateXPMBoard(appData.liteBackTextureFile, 1);
1122 CreateXPMBoard(appData.darkBackTextureFile, 0);
1124 if (appData.pngDirectory[0] != NULLCHAR) { // for now do in parallel
1129 /* Create regular pieces */
1130 if (!useImages) CreatePieces();
1135 InitDrawingParams ()
1137 MakeColors(); CreateGCs(True);
1142 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1143 { // detervtomine what fonts to use, and create them
1147 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1148 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1149 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1150 appData.font = fontTable[MESSAGE_FONT][squareSize];
1151 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1152 appData.coordFont = fontTable[COORD_FONT][squareSize];
1155 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1156 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1157 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1158 fontSet = CreateFontSet(appData.font);
1159 clockFontSet = CreateFontSet(appData.clockFont);
1161 /* For the coordFont, use the 0th font of the fontset. */
1162 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1163 XFontStruct **font_struct_list;
1164 XFontSetExtents *fontSize;
1165 char **font_name_list;
1166 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1167 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1168 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1169 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1170 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1173 appData.font = FindFont(appData.font, fontPxlSize);
1174 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1175 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1176 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1177 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1178 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1179 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1180 // textHeight in !NLS mode!
1182 countFontID = coordFontID; // [HGM] holdings
1183 countFontStruct = coordFontStruct;
1185 xdb = XtDatabase(xDisplay);
1187 XrmPutLineResource(&xdb, "*international: True");
1188 vTo.size = sizeof(XFontSet);
1189 vTo.addr = (XtPointer) &fontSet;
1190 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1192 XrmPutStringResource(&xdb, "*font", appData.font);
1197 PrintArg (ArgType t)
1202 case ArgInt: p = " N"; break;
1203 case ArgString: p = " STR"; break;
1204 case ArgBoolean: p = " TF"; break;
1205 case ArgSettingsFilename:
1206 case ArgFilename: p = " FILE"; break;
1207 case ArgX: p = " Nx"; break;
1208 case ArgY: p = " Ny"; break;
1209 case ArgAttribs: p = " TEXTCOL"; break;
1210 case ArgColor: p = " COL"; break;
1211 case ArgFont: p = " FONT"; break;
1212 case ArgBoardSize: p = " SIZE"; break;
1213 case ArgFloat: p = " FLOAT"; break;
1218 case ArgCommSettings:
1229 ArgDescriptor *q, *p = argDescriptors+5;
1230 printf("\nXBoard accepts the following options:\n"
1231 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1232 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1233 " SIZE = board-size spec(s)\n"
1234 " Within parentheses are short forms, or options to set to true or false.\n"
1235 " Persistent options (saved in the settings file) are marked with *)\n\n");
1237 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1238 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1239 if(p->save) strcat(buf+len, "*");
1240 for(q=p+1; q->argLoc == p->argLoc; q++) {
1241 if(q->argName[0] == '-') continue;
1242 strcat(buf+len, q == p+1 ? " (" : " ");
1243 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1245 if(q != p+1) strcat(buf+len, ")");
1247 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1250 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1254 main (int argc, char **argv)
1256 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1257 XSetWindowAttributes window_attributes;
1259 Dimension boardWidth, boardHeight, w, h;
1261 int forceMono = False;
1263 srandom(time(0)); // [HGM] book: make random truly random
1265 setbuf(stdout, NULL);
1266 setbuf(stderr, NULL);
1269 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1270 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1274 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1279 programName = strrchr(argv[0], '/');
1280 if (programName == NULL)
1281 programName = argv[0];
1286 XtSetLanguageProc(NULL, NULL, NULL);
1287 if (appData.debugMode) {
1288 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1291 bindtextdomain(PACKAGE, LOCALEDIR);
1292 textdomain(PACKAGE);
1295 appData.boardSize = "";
1296 InitAppData(ConvertToLine(argc, argv));
1298 if (p == NULL) p = "/tmp";
1299 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1300 gameCopyFilename = (char*) malloc(i);
1301 gamePasteFilename = (char*) malloc(i);
1302 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1303 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1305 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1306 static char buf[MSG_SIZ];
1307 EscapeExpand(buf, appData.firstInitString);
1308 appData.firstInitString = strdup(buf);
1309 EscapeExpand(buf, appData.secondInitString);
1310 appData.secondInitString = strdup(buf);
1311 EscapeExpand(buf, appData.firstComputerString);
1312 appData.firstComputerString = strdup(buf);
1313 EscapeExpand(buf, appData.secondComputerString);
1314 appData.secondComputerString = strdup(buf);
1317 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1320 if (chdir(chessDir) != 0) {
1321 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1327 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1328 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1329 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1330 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1333 setbuf(debugFP, NULL);
1336 /* [HGM,HR] make sure board size is acceptable */
1337 if(appData.NrFiles > BOARD_FILES ||
1338 appData.NrRanks > BOARD_RANKS )
1339 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1342 /* This feature does not work; animation needs a rewrite */
1343 appData.highlightDragging = FALSE;
1347 gameInfo.variant = StringToVariant(appData.variant);
1348 InitPosition(FALSE);
1351 XtAppInitialize(&appContext, "XBoard", shellOptions,
1352 XtNumber(shellOptions),
1353 &argc, argv, xboardResources, NULL, 0);
1355 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1356 clientResources, XtNumber(clientResources),
1359 xDisplay = XtDisplay(shellWidget);
1360 xScreen = DefaultScreen(xDisplay);
1361 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1364 * determine size, based on supplied or remembered -size, or screen size
1366 if (isdigit(appData.boardSize[0])) {
1367 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1368 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1369 &fontPxlSize, &smallLayout, &tinyLayout);
1371 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1372 programName, appData.boardSize);
1376 /* Find some defaults; use the nearest known size */
1377 SizeDefaults *szd, *nearest;
1378 int distance = 99999;
1379 nearest = szd = sizeDefaults;
1380 while (szd->name != NULL) {
1381 if (abs(szd->squareSize - squareSize) < distance) {
1383 distance = abs(szd->squareSize - squareSize);
1384 if (distance == 0) break;
1388 if (i < 2) lineGap = nearest->lineGap;
1389 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1390 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1391 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1392 if (i < 6) smallLayout = nearest->smallLayout;
1393 if (i < 7) tinyLayout = nearest->tinyLayout;
1396 SizeDefaults *szd = sizeDefaults;
1397 if (*appData.boardSize == NULLCHAR) {
1398 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1399 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1402 if (szd->name == NULL) szd--;
1403 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1405 while (szd->name != NULL &&
1406 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1407 if (szd->name == NULL) {
1408 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1409 programName, appData.boardSize);
1413 squareSize = szd->squareSize;
1414 lineGap = szd->lineGap;
1415 clockFontPxlSize = szd->clockFontPxlSize;
1416 coordFontPxlSize = szd->coordFontPxlSize;
1417 fontPxlSize = szd->fontPxlSize;
1418 smallLayout = szd->smallLayout;
1419 tinyLayout = szd->tinyLayout;
1420 // [HGM] font: use defaults from settings file if available and not overruled
1423 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1424 if (strlen(appData.pixmapDirectory) > 0) {
1425 p = ExpandPathName(appData.pixmapDirectory);
1427 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1428 appData.pixmapDirectory);
1431 if (appData.debugMode) {
1432 fprintf(stderr, _("\
1433 XBoard square size (hint): %d\n\
1434 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1436 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1437 if (appData.debugMode) {
1438 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1441 defaultLineGap = lineGap;
1442 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1444 /* [HR] height treated separately (hacked) */
1445 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1446 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1449 * Determine what fonts to use.
1451 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1454 * Detect if there are not enough colors available and adapt.
1456 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1457 appData.monoMode = True;
1460 forceMono = MakeColors();
1463 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1465 appData.monoMode = True;
1468 if (appData.monoMode && appData.debugMode) {
1469 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1470 (unsigned long) XWhitePixel(xDisplay, xScreen),
1471 (unsigned long) XBlackPixel(xDisplay, xScreen));
1474 ParseIcsTextColors();
1476 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1482 layoutName = "tinyLayout";
1483 } else if (smallLayout) {
1484 layoutName = "smallLayout";
1486 layoutName = "normalLayout";
1489 optList = BoardPopUp(squareSize, lineGap, (void*)
1495 boardWidget = optList[W_BOARD].handle;
1496 menuBarWidget = optList[W_MENU].handle;
1497 dropMenu = optList[W_DROP].handle;
1498 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1499 formWidget = XtParent(boardWidget);
1500 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1501 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1502 XtGetValues(optList[W_WHITE].handle, args, 2);
1503 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1504 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1505 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1506 XtGetValues(optList[W_PAUSE].handle, args, 2);
1508 AppendEnginesToMenu(appData.recentEngineList);
1510 xBoardWindow = XtWindow(boardWidget);
1512 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1513 // not need to go into InitDrawingSizes().
1516 * Create X checkmark bitmap and initialize option menu checks.
1518 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1519 checkmark_bits, checkmark_width, checkmark_height);
1525 ReadBitmap(&wIconPixmap, "icon_white.bm",
1526 icon_white_bits, icon_white_width, icon_white_height);
1527 ReadBitmap(&bIconPixmap, "icon_black.bm",
1528 icon_black_bits, icon_black_width, icon_black_height);
1529 iconPixmap = wIconPixmap;
1531 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1532 XtSetValues(shellWidget, args, i);
1535 * Create a cursor for the board widget.
1537 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1538 XChangeWindowAttributes(xDisplay, xBoardWindow,
1539 CWCursor, &window_attributes);
1542 * Inhibit shell resizing.
1546 cairoAnimate = *appData.pngDirectory && useTexture == 3
1547 && strstr(appData.liteBackTextureFile, ".png") && strstr(appData.darkBackTextureFile, ".png");
1549 shellArgs[0].value = (XtArgVal) &w;
1550 shellArgs[1].value = (XtArgVal) &h;
1551 XtGetValues(shellWidget, shellArgs, 2);
1552 shellArgs[4].value = shellArgs[2].value = w;
1553 shellArgs[5].value = shellArgs[3].value = h;
1554 if(!cairoAnimate) XtSetValues(shellWidget, &shellArgs[2], 4);
1555 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1556 marginH = h - boardHeight;
1558 CatchDeleteWindow(shellWidget, "QuitProc");
1563 if(appData.logoSize)
1564 { // locate and read user logo
1566 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1567 ASSIGN(userLogo, buf);
1570 if (appData.animate || appData.animateDragging)
1573 XtAugmentTranslations(formWidget,
1574 XtParseTranslationTable(globalTranslations));
1576 XtAddEventHandler(formWidget, KeyPressMask, False,
1577 (XtEventHandler) MoveTypeInProc, NULL);
1578 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1579 (XtEventHandler) EventProc, NULL);
1581 /* [AS] Restore layout */
1582 if( wpMoveHistory.visible ) {
1586 if( wpEvalGraph.visible )
1591 if( wpEngineOutput.visible ) {
1592 EngineOutputPopUp();
1597 if (errorExitStatus == -1) {
1598 if (appData.icsActive) {
1599 /* We now wait until we see "login:" from the ICS before
1600 sending the logon script (problems with timestamp otherwise) */
1601 /*ICSInitScript();*/
1602 if (appData.icsInputBox) ICSInputBoxPopUp();
1606 signal(SIGWINCH, TermSizeSigHandler);
1608 signal(SIGINT, IntSigHandler);
1609 signal(SIGTERM, IntSigHandler);
1610 if (*appData.cmailGameName != NULLCHAR) {
1611 signal(SIGUSR1, CmailSigHandler);
1615 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1618 // XtSetKeyboardFocus(shellWidget, formWidget);
1619 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1621 XtAppMainLoop(appContext);
1622 if (appData.debugMode) fclose(debugFP); // [DM] debug
1627 TermSizeSigHandler (int sig)
1633 IntSigHandler (int sig)
1639 CmailSigHandler (int sig)
1644 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1646 /* Activate call-back function CmailSigHandlerCallBack() */
1647 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1649 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1653 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1656 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1658 /**** end signal code ****/
1661 #define Abs(n) ((n)<0 ? -(n) : (n))
1665 InsertPxlSize (char *pattern, int targetPxlSize)
1667 char *base_fnt_lst, strInt[12], *p, *q;
1668 int alternatives, i, len, strIntLen;
1671 * Replace the "*" (if present) in the pixel-size slot of each
1672 * alternative with the targetPxlSize.
1676 while ((p = strchr(p, ',')) != NULL) {
1680 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1681 strIntLen = strlen(strInt);
1682 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1686 while (alternatives--) {
1687 char *comma = strchr(p, ',');
1688 for (i=0; i<14; i++) {
1689 char *hyphen = strchr(p, '-');
1691 if (comma && hyphen > comma) break;
1692 len = hyphen + 1 - p;
1693 if (i == 7 && *p == '*' && len == 2) {
1695 memcpy(q, strInt, strIntLen);
1705 len = comma + 1 - p;
1712 return base_fnt_lst;
1716 CreateFontSet (char *base_fnt_lst)
1719 char **missing_list;
1723 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1724 &missing_list, &missing_count, &def_string);
1725 if (appData.debugMode) {
1727 XFontStruct **font_struct_list;
1728 char **font_name_list;
1729 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1731 fprintf(debugFP, " got list %s, locale %s\n",
1732 XBaseFontNameListOfFontSet(fntSet),
1733 XLocaleOfFontSet(fntSet));
1734 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1735 for (i = 0; i < count; i++) {
1736 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1739 for (i = 0; i < missing_count; i++) {
1740 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1743 if (fntSet == NULL) {
1744 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1749 #else // not ENABLE_NLS
1751 * Find a font that matches "pattern" that is as close as
1752 * possible to the targetPxlSize. Prefer fonts that are k
1753 * pixels smaller to fonts that are k pixels larger. The
1754 * pattern must be in the X Consortium standard format,
1755 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1756 * The return value should be freed with XtFree when no
1760 FindFont (char *pattern, int targetPxlSize)
1762 char **fonts, *p, *best, *scalable, *scalableTail;
1763 int i, j, nfonts, minerr, err, pxlSize;
1765 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1767 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1768 programName, pattern);
1775 for (i=0; i<nfonts; i++) {
1778 if (*p != '-') continue;
1780 if (*p == NULLCHAR) break;
1781 if (*p++ == '-') j++;
1783 if (j < 7) continue;
1786 scalable = fonts[i];
1789 err = pxlSize - targetPxlSize;
1790 if (Abs(err) < Abs(minerr) ||
1791 (minerr > 0 && err < 0 && -err == minerr)) {
1797 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1798 /* If the error is too big and there is a scalable font,
1799 use the scalable font. */
1800 int headlen = scalableTail - scalable;
1801 p = (char *) XtMalloc(strlen(scalable) + 10);
1802 while (isdigit(*scalableTail)) scalableTail++;
1803 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1805 p = (char *) XtMalloc(strlen(best) + 2);
1806 safeStrCpy(p, best, strlen(best)+1 );
1808 if (appData.debugMode) {
1809 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1810 pattern, targetPxlSize, p);
1812 XFreeFontNames(fonts);
1819 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1820 // must be called before all non-first callse to CreateGCs()
1821 XtReleaseGC(shellWidget, highlineGC);
1822 XtReleaseGC(shellWidget, lightSquareGC);
1823 XtReleaseGC(shellWidget, darkSquareGC);
1824 XtReleaseGC(shellWidget, lineGC);
1825 if (appData.monoMode) {
1826 if (DefaultDepth(xDisplay, xScreen) == 1) {
1827 XtReleaseGC(shellWidget, wbPieceGC);
1829 XtReleaseGC(shellWidget, bwPieceGC);
1832 XtReleaseGC(shellWidget, prelineGC);
1833 XtReleaseGC(shellWidget, wdPieceGC);
1834 XtReleaseGC(shellWidget, wlPieceGC);
1835 XtReleaseGC(shellWidget, bdPieceGC);
1836 XtReleaseGC(shellWidget, blPieceGC);
1841 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1843 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1844 | GCBackground | GCFunction | GCPlaneMask;
1845 gc_values->foreground = foreground;
1846 gc_values->background = background;
1847 return XtGetGC(shellWidget, value_mask, gc_values);
1851 CreateGCs (int redo)
1853 XGCValues gc_values;
1855 Pixel white = XWhitePixel(xDisplay, xScreen);
1856 Pixel black = XBlackPixel(xDisplay, xScreen);
1858 gc_values.plane_mask = AllPlanes;
1859 gc_values.line_width = lineGap;
1860 gc_values.line_style = LineSolid;
1861 gc_values.function = GXcopy;
1864 DeleteGCs(); // called a second time; clean up old GCs first
1865 } else { // [HGM] grid and font GCs created on first call only
1866 coordGC = CreateOneGC(&gc_values, black, white);
1867 XSetFont(xDisplay, coordGC, coordFontID);
1869 // [HGM] make font for holdings counts (white on black)
1870 countGC = CreateOneGC(&gc_values, white, black);
1871 XSetFont(xDisplay, countGC, countFontID);
1873 lineGC = CreateOneGC(&gc_values, black, black);
1875 if (appData.monoMode) {
1877 highlineGC = CreateOneGC(&gc_values, white, white);
1878 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1879 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1881 if (DefaultDepth(xDisplay, xScreen) == 1) {
1882 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1883 gc_values.function = GXcopyInverted;
1884 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1885 gc_values.function = GXcopy;
1886 if (XBlackPixel(xDisplay, xScreen) == 1) {
1887 bwPieceGC = darkSquareGC;
1888 wbPieceGC = copyInvertedGC;
1890 bwPieceGC = copyInvertedGC;
1891 wbPieceGC = lightSquareGC;
1896 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1897 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1898 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1899 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1900 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1901 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1902 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1903 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1908 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1916 fp = fopen(filename, "rb");
1918 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1925 for (y=0; y<h; ++y) {
1926 for (x=0; x<h; ++x) {
1931 XPutPixel(xim, x, y, blackPieceColor);
1933 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1936 XPutPixel(xim, x, y, darkSquareColor);
1938 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1941 XPutPixel(xim, x, y, whitePieceColor);
1943 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1946 XPutPixel(xim, x, y, lightSquareColor);
1948 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1956 /* create Pixmap of piece */
1957 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1959 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1962 /* create Pixmap of clipmask
1963 Note: We assume the white/black pieces have the same
1964 outline, so we make only 6 masks. This is okay
1965 since the XPM clipmask routines do the same. */
1967 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1969 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1972 /* now create the 1-bit version */
1973 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1976 values.foreground = 1;
1977 values.background = 0;
1979 /* Don't use XtGetGC, not read only */
1980 maskGC = XCreateGC(xDisplay, *mask,
1981 GCForeground | GCBackground, &values);
1982 XCopyPlane(xDisplay, temp, *mask, maskGC,
1983 0, 0, squareSize, squareSize, 0, 0, 1);
1984 XFreePixmap(xDisplay, temp);
1989 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1997 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2002 /* The XSynchronize calls were copied from CreatePieces.
2003 Not sure if needed, but can't hurt */
2004 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2007 /* temp needed by loadXIM() */
2008 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2009 0, 0, ss, ss, AllPlanes, XYPixmap);
2011 if (strlen(appData.pixmapDirectory) == 0) {
2015 if (appData.monoMode) {
2016 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2020 fprintf(stderr, _("\nLoading XIMs...\n"));
2022 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2023 fprintf(stderr, "%d", piece+1);
2024 for (kind=0; kind<4; kind++) {
2025 fprintf(stderr, ".");
2026 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2027 ExpandPathName(appData.pixmapDirectory),
2028 piece <= (int) WhiteKing ? "" : "w",
2029 pieceBitmapNames[piece],
2031 ximPieceBitmap[kind][piece] =
2032 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2033 0, 0, ss, ss, AllPlanes, XYPixmap);
2034 if (appData.debugMode)
2035 fprintf(stderr, _("(File:%s:) "), buf);
2036 loadXIM(ximPieceBitmap[kind][piece],
2038 &(xpmPieceBitmap2[kind][piece]),
2039 &(ximMaskPm2[piece]));
2040 if(piece <= (int)WhiteKing)
2041 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2043 fprintf(stderr," ");
2045 /* Load light and dark squares */
2046 /* If the LSQ and DSQ pieces don't exist, we will
2047 draw them with solid squares. */
2048 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2049 if (access(buf, 0) != 0) {
2053 fprintf(stderr, _("light square "));
2055 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2056 0, 0, ss, ss, AllPlanes, XYPixmap);
2057 if (appData.debugMode)
2058 fprintf(stderr, _("(File:%s:) "), buf);
2060 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2061 fprintf(stderr, _("dark square "));
2062 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2063 ExpandPathName(appData.pixmapDirectory), ss);
2064 if (appData.debugMode)
2065 fprintf(stderr, _("(File:%s:) "), buf);
2067 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2068 0, 0, ss, ss, AllPlanes, XYPixmap);
2069 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2070 xpmJailSquare = xpmLightSquare;
2072 fprintf(stderr, _("Done.\n"));
2074 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2077 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2081 CreateXPMBoard (char *s, int kind)
2085 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2086 if(strstr(s, ".png")) {
2087 cairo_surface_t *img = cairo_image_surface_create_from_png (s);
2089 useTexture |= kind + 1; pngBoardBitmap[kind] = img;
2090 textureW[kind] = cairo_image_surface_get_width (img);
2091 textureH[kind] = cairo_image_surface_get_height (img);
2094 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2095 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2101 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2102 // thisroutine has to be called t free the old piece pixmaps
2104 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2105 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2107 XFreePixmap(xDisplay, xpmLightSquare);
2108 XFreePixmap(xDisplay, xpmDarkSquare);
2117 u_int ss = squareSize;
2119 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2120 XpmColorSymbol symbols[4];
2121 static int redo = False;
2123 if(redo) FreeXPMPieces(); else redo = 1;
2125 /* The XSynchronize calls were copied from CreatePieces.
2126 Not sure if needed, but can't hurt */
2127 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2129 /* Setup translations so piece colors match square colors */
2130 symbols[0].name = "light_piece";
2131 symbols[0].value = appData.whitePieceColor;
2132 symbols[1].name = "dark_piece";
2133 symbols[1].value = appData.blackPieceColor;
2134 symbols[2].name = "light_square";
2135 symbols[2].value = appData.lightSquareColor;
2136 symbols[3].name = "dark_square";
2137 symbols[3].value = appData.darkSquareColor;
2139 attr.valuemask = XpmColorSymbols;
2140 attr.colorsymbols = symbols;
2141 attr.numsymbols = 4;
2143 if (appData.monoMode) {
2144 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2148 if (strlen(appData.pixmapDirectory) == 0) {
2149 XpmPieces* pieces = builtInXpms;
2152 while (pieces->size != squareSize && pieces->size) pieces++;
2153 if (!pieces->size) {
2154 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2157 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2158 for (kind=0; kind<4; kind++) {
2160 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2161 pieces->xpm[piece][kind],
2162 &(xpmPieceBitmap2[kind][piece]),
2163 NULL, &attr)) != 0) {
2164 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2168 if(piece <= (int) WhiteKing)
2169 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2173 xpmJailSquare = xpmLightSquare;
2177 fprintf(stderr, _("\nLoading XPMs...\n"));
2180 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2181 fprintf(stderr, "%d ", piece+1);
2182 for (kind=0; kind<4; kind++) {
2183 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2184 ExpandPathName(appData.pixmapDirectory),
2185 piece > (int) WhiteKing ? "w" : "",
2186 pieceBitmapNames[piece],
2188 if (appData.debugMode) {
2189 fprintf(stderr, _("(File:%s:) "), buf);
2191 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2192 &(xpmPieceBitmap2[kind][piece]),
2193 NULL, &attr)) != 0) {
2194 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2195 // [HGM] missing: read of unorthodox piece failed; substitute King.
2196 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2197 ExpandPathName(appData.pixmapDirectory),
2199 if (appData.debugMode) {
2200 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2202 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2203 &(xpmPieceBitmap2[kind][piece]),
2207 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2212 if(piece <= (int) WhiteKing)
2213 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2216 /* Load light and dark squares */
2217 /* If the LSQ and DSQ pieces don't exist, we will
2218 draw them with solid squares. */
2219 fprintf(stderr, _("light square "));
2220 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2221 if (access(buf, 0) != 0) {
2225 if (appData.debugMode)
2226 fprintf(stderr, _("(File:%s:) "), buf);
2228 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2229 &xpmLightSquare, NULL, &attr)) != 0) {
2230 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2233 fprintf(stderr, _("dark square "));
2234 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2235 ExpandPathName(appData.pixmapDirectory), ss);
2236 if (appData.debugMode) {
2237 fprintf(stderr, _("(File:%s:) "), buf);
2239 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2240 &xpmDarkSquare, NULL, &attr)) != 0) {
2241 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2245 xpmJailSquare = xpmLightSquare;
2246 fprintf(stderr, _("Done.\n"));
2248 oldVariant = -1; // kludge to force re-makig of animation masks
2249 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2252 #endif /* HAVE_LIBXPM */
2254 char *pngPieceNames[] = // must be in same order as internal piece encoding
2255 { "Pawn", "Knight", "Bishop", "Rook", "Queen", "Advisor", "Elephant", "Archbishop", "Marshall", "Gold", "Commoner",
2256 "Canon", "Nightrider", "CrownedBishop", "CrownedRook", "Princess", "Chancellor", "Hawk", "Lance", "Cobra", "Unicorn", "King",
2257 "GoldKnight", "GoldLance", "GoldPawn", "GoldSilver", NULL
2261 ScaleOnePiece (char *name, int color, int piece)
2265 cairo_surface_t *img, *cs;
2267 static cairo_surface_t *pngPieceImages[2][(int)BlackPawn+4]; // png 256 x 256 images
2269 if((img = pngPieceImages[color][piece]) == NULL) { // if PNG file for this piece was not yet read, read it now and store it
2270 snprintf(buf, MSG_SIZ, "%s/%s%s.png", appData.pngDirectory, color ? "Black" : "White", pngPieceNames[piece]);
2271 pngPieceImages[color][piece] = img = cairo_image_surface_create_from_png (buf);
2272 w = cairo_image_surface_get_width (img);
2273 h = cairo_image_surface_get_height (img);
2274 if(w != 64 || h != 64) { printf("Bad png size %dx%d in %s\n", w, h, buf); exit(1); }
2276 // create new bitmap to hold scaled piece image (and remove any old)
2277 if(pngPieceBitmaps2[color][piece]) cairo_surface_destroy (pngPieceBitmaps2[color][piece]);
2278 pngPieceBitmaps2[color][piece] = cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
2279 if(piece <= WhiteKing) pngPieceBitmaps[color][piece] = cs;
2280 // scaled copying of the raw png image
2281 cr = cairo_create(cs);
2282 cairo_scale(cr, squareSize/64., squareSize/64.);
2283 cairo_set_source_surface (cr, img, 0, 0);
2293 for(p=0; pngPieceNames[p]; p++) {
2294 ScaleOnePiece(pngPieceNames[p], 0, p);
2295 ScaleOnePiece(pngPieceNames[p], 1, p);
2300 /* No built-in bitmaps */
2305 u_int ss = squareSize;
2307 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2310 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2311 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2312 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2313 pieceBitmapNames[piece],
2314 ss, kind == SOLID ? 's' : 'o');
2315 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2316 if(piece <= (int)WhiteKing)
2317 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2321 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2325 /* With built-in bitmaps */
2329 BuiltInBits* bib = builtInBits;
2332 u_int ss = squareSize;
2334 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2337 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2339 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2340 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2341 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2342 pieceBitmapNames[piece],
2343 ss, kind == SOLID ? 's' : 'o');
2344 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2345 bib->bits[kind][piece], ss, ss);
2346 if(piece <= (int)WhiteKing)
2347 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2351 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2357 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2362 char msg[MSG_SIZ], fullname[MSG_SIZ];
2364 if (*appData.bitmapDirectory != NULLCHAR) {
2365 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2366 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2367 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2368 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2369 &w, &h, pm, &x_hot, &y_hot);
2370 fprintf(stderr, "load %s\n", name);
2371 if (errcode != BitmapSuccess) {
2373 case BitmapOpenFailed:
2374 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2376 case BitmapFileInvalid:
2377 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2379 case BitmapNoMemory:
2380 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2384 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2388 fprintf(stderr, _("%s: %s...using built-in\n"),
2390 } else if (w != wreq || h != hreq) {
2392 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2393 programName, fullname, w, h, wreq, hreq);
2399 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2409 if (lineGap == 0) return;
2411 /* [HR] Split this into 2 loops for non-square boards. */
2413 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2414 gridSegments[i].x1 = 0;
2415 gridSegments[i].x2 =
2416 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2417 gridSegments[i].y1 = gridSegments[i].y2
2418 = lineGap / 2 + (i * (squareSize + lineGap));
2421 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2422 gridSegments[j + i].y1 = 0;
2423 gridSegments[j + i].y2 =
2424 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2425 gridSegments[j + i].x1 = gridSegments[j + i].x2
2426 = lineGap / 2 + (j * (squareSize + lineGap));
2431 MarkMenuItem (char *menuRef, int state)
2433 MenuItem *item = MenuNameToItem(menuRef);
2437 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2438 XtSetValues(item->handle, args, 1);
2443 EnableNamedMenuItem (char *menuRef, int state)
2445 MenuItem *item = MenuNameToItem(menuRef);
2447 if(item) XtSetSensitive(item->handle, state);
2451 EnableButtonBar (int state)
2453 XtSetSensitive(optList[W_BUTTON].handle, state);
2458 SetMenuEnables (Enables *enab)
2460 while (enab->name != NULL) {
2461 EnableNamedMenuItem(enab->name, enab->value);
2467 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2468 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2470 if(*nprms == 0) return;
2471 item = MenuNameToItem(prms[0]);
2472 if(item) ((MenuProc *) item->proc) ();
2476 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2478 RecentEngineEvent((int) (intptr_t) addr);
2482 AppendMenuItem (char *msg, int n)
2484 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2496 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2497 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2498 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2499 dmEnables[i].piece);
2500 XtSetSensitive(entry, p != NULL || !appData.testLegality
2501 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2502 && !appData.icsActive));
2504 while (p && *p++ == dmEnables[i].piece) count++;
2505 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2507 XtSetArg(args[j], XtNlabel, label); j++;
2508 XtSetValues(entry, args, j);
2514 do_flash_delay (unsigned long msec)
2520 DoDrawBorder (cairo_surface_t *cs, int x, int y, int type)
2525 cr = cairo_create(cs);
2526 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
2527 cairo_rectangle(cr, x, y, squareSize+lineGap, squareSize+lineGap);
2528 SetPen(cr, lineGap, type == 1 ? appData.highlightSquareColor : appData.premoveHighlightColor, 0);
2533 DrawBorder (int x, int y, int type)
2535 DoDrawBorder(csBoardWindow, x, y, type);
2536 DoDrawBorder(csBoardBackup, x, y, type);
2540 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2542 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2543 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2545 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2546 if(textureW[kind] < W*squareSize)
2547 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2549 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2550 if(textureH[kind] < H*squareSize)
2551 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2553 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2558 DrawLogo (void *handle, void *logo)
2560 cairo_surface_t *img, *cs;
2564 if(!logo || !handle) return;
2565 cs = cairo_xlib_surface_create(xDisplay, XtWindow(handle), DefaultVisual(xDisplay, 0), appData.logoSize, appData.logoSize/2);
2566 img = cairo_image_surface_create_from_png (logo);
2567 w = cairo_image_surface_get_width (img);
2568 h = cairo_image_surface_get_height (img);
2569 cr = cairo_create(cs);
2570 cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
2571 cairo_set_source_surface (cr, img, 0, 0);
2574 cairo_surface_destroy (img);
2575 cairo_surface_destroy (cs);
2579 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2580 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2582 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2583 if(pngBoardBitmap[color]) {
2585 if(!fac && !cairoAnimate) return;
2587 cr = cairo_create (fac ? csBoardWindow : (cairo_surface_t *) dest);
2588 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2589 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2590 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2594 cr = cairo_create (csBoardBackup);
2595 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2596 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2597 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2602 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2603 squareSize, squareSize, x*fac, y*fac);
2606 cairo_t *cr = cairo_create (csBoardWindow);
2609 case 0: col = appData.darkSquareColor; break;
2610 case 1: col = appData.lightSquareColor; break;
2611 case 2: col = "#000000"; break;
2613 SetPen(cr, 2.0, col, 0);
2614 cairo_rectangle (cr, x, y, squareSize, squareSize);
2617 cr = cairo_create (csBoardBackup);
2618 SetPen(cr, 2.0, col, 0);
2619 cairo_rectangle (cr, x, y, squareSize, squareSize);
2623 if (useImages && useImageSqs) {
2627 pm = xpmLightSquare;
2632 case 2: /* neutral */
2634 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2637 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2638 squareSize, squareSize, x*fac, y*fac);
2648 case 2: /* neutral */
2653 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2658 I split out the routines to draw a piece so that I could
2659 make a generic flash routine.
2662 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2664 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2665 switch (square_color) {
2667 case 2: /* neutral */
2669 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2670 ? *pieceToOutline(piece)
2671 : *pieceToSolid(piece),
2672 dest, bwPieceGC, 0, 0,
2673 squareSize, squareSize, x, y);
2676 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2677 ? *pieceToSolid(piece)
2678 : *pieceToOutline(piece),
2679 dest, wbPieceGC, 0, 0,
2680 squareSize, squareSize, x, y);
2686 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2688 switch (square_color) {
2690 case 2: /* neutral */
2692 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2693 ? *pieceToOutline(piece)
2694 : *pieceToSolid(piece),
2695 dest, bwPieceGC, 0, 0,
2696 squareSize, squareSize, x, y, 1);
2699 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2700 ? *pieceToSolid(piece)
2701 : *pieceToOutline(piece),
2702 dest, wbPieceGC, 0, 0,
2703 squareSize, squareSize, x, y, 1);
2709 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2711 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2712 switch (square_color) {
2714 XCopyPlane(xDisplay, *pieceToSolid(piece),
2715 dest, (int) piece < (int) BlackPawn
2716 ? wlPieceGC : blPieceGC, 0, 0,
2717 squareSize, squareSize, x, y, 1);
2720 XCopyPlane(xDisplay, *pieceToSolid(piece),
2721 dest, (int) piece < (int) BlackPawn
2722 ? wdPieceGC : bdPieceGC, 0, 0,
2723 squareSize, squareSize, x, y, 1);
2725 case 2: /* neutral */
2727 break; // should never contain pieces
2732 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2734 int kind, p = piece;
2736 switch (square_color) {
2738 case 2: /* neutral */
2740 if ((int)piece < (int) BlackPawn) {
2748 if ((int)piece < (int) BlackPawn) {
2756 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2757 if(useTexture & square_color+1) {
2758 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2759 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2760 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2761 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2762 XSetClipMask(xDisplay, wlPieceGC, None);
2763 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2765 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2766 dest, wlPieceGC, 0, 0,
2767 squareSize, squareSize, x, y);
2771 pngDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2773 int kind, p = piece;
2776 if ((int)piece < (int) BlackPawn) {
2782 if(appData.upsideDown && flipView) { p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2783 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2785 cr = cairo_create (csBoardWindow);
2786 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2789 cr = cairo_create (csBoardBackup);
2790 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2795 typedef void (*DrawFunc)();
2800 if (appData.monoMode) {
2801 if (DefaultDepth(xDisplay, xScreen) == 1) {
2802 return monoDrawPiece_1bit;
2804 return monoDrawPiece;
2806 } else if(appData.pngDirectory[0]) {
2807 return pngDrawPiece;
2810 return colorDrawPieceImage;
2812 return colorDrawPiece;
2817 DoDrawDot (int marker, int x, int y, int r, cairo_surface_t *cs)
2821 cr = cairo_create(cs);
2822 cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
2823 if(appData.monoMode) {
2824 SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
2825 cairo_stroke_preserve(cr);
2826 SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
2828 SetPen(cr, 2, marker == 2 ? "#FF0000" : "#FFFF00", 0);
2837 DrawDot (int marker, int x, int y, int r)
2839 DoDrawDot(marker, x, y, r, csBoardWindow);
2840 DoDrawDot(marker, x, y, r, csBoardBackup);
2844 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2845 { // basic front-end board-draw function: takes care of everything that can be in square:
2846 // piece, background, coordinate/count, marker dot
2847 int direction, font_ascent, font_descent;
2848 XCharStruct overall;
2851 if (piece == EmptySquare) {
2852 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2854 drawfunc = ChooseDrawFunc();
2855 drawfunc(piece, square_color, x, y, xBoardWindow);
2858 if(align) { // square carries inscription (coord or piece count)
2860 GC hGC = align < 3 ? coordGC : countGC;
2861 // first calculate where it goes
2862 XTextExtents(countFontStruct, string, 1, &direction,
2863 &font_ascent, &font_descent, &overall);
2865 xx += squareSize - overall.width - 2;
2866 yy += squareSize - font_descent - 1;
2867 } else if (align == 2) {
2868 xx += 2, yy += font_ascent + 1;
2869 } else if (align == 3) {
2870 xx += squareSize - overall.width - 2;
2871 yy += font_ascent + 1;
2872 } else if (align == 4) {
2873 xx += 2, yy += font_ascent + 1;
2876 if (appData.monoMode) {
2877 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2879 if(*appData.pngDirectory) {
2880 cairo_t *cr = cairo_create (csBoardWindow);
2881 cairo_select_font_face (cr, "Sans",
2882 CAIRO_FONT_SLANT_NORMAL,
2883 CAIRO_FONT_WEIGHT_BOLD);
2885 cairo_set_font_size (cr, squareSize/4);
2887 cairo_move_to (cr, xx-1, yy);
2888 if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2889 else cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
2890 cairo_show_text (cr, string);
2895 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2899 if(marker) { // print fat marker dot, if requested
2900 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2905 FlashDelay (int flash_delay)
2907 XSync(xDisplay, False);
2908 if(flash_delay) do_flash_delay(flash_delay);
2912 Fraction (int x, int start, int stop)
2914 double f = ((double) x - start)/(stop - start);
2915 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2919 static WindowPlacement wpNew;
2922 CoDrag (Widget sh, WindowPlacement *wp)
2925 int j=0, touch=0, fudge = 2;
2926 GetActualPlacement(sh, wp);
2927 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2928 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2929 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2930 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2931 if(!touch ) return; // only windows that touch co-move
2932 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2933 int heightInc = wpNew.height - wpMain.height;
2934 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2935 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2936 wp->y += fracTop * heightInc;
2937 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2938 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2939 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2940 int widthInc = wpNew.width - wpMain.width;
2941 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2942 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2943 wp->y += fracLeft * widthInc;
2944 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2945 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2947 wp->x += wpNew.x - wpMain.x;
2948 wp->y += wpNew.y - wpMain.y;
2949 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2950 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2951 XtSetArg(args[j], XtNx, wp->x); j++;
2952 XtSetArg(args[j], XtNy, wp->y); j++;
2953 XtSetValues(sh, args, j);
2957 ReSize (WindowPlacement *wp)
2960 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
2961 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
2962 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
2963 if(sqy < sqx) sqx = sqy;
2964 if(sqx != squareSize) {
2965 squareSize = sqx; // adopt new square size
2967 CreatePNGPieces(); // make newly scaled pieces
2968 InitDrawingSizes(0, 0); // creates grid etc.
2972 static XtIntervalId delayedDragID = 0;
2981 GetActualPlacement(shellWidget, &wpNew);
2982 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2983 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
2984 busy = 0; return; // false alarm
2987 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2988 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2989 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2990 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2992 DrawPosition(True, NULL);
2993 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3001 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3003 XtAppAddTimeOut(appContext, 100, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3007 EventProc (Widget widget, caddr_t unused, XEvent *event)
3009 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
3010 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3013 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
3016 Color (char *col, int n)
3019 sscanf(col, "#%x", &c);
3025 SetPen (cairo_t *cr, float w, char *col, int dash)
3027 static const double dotted[] = {4.0, 4.0};
3028 static int len = sizeof(dotted) / sizeof(dotted[0]);
3029 cairo_set_line_width (cr, w);
3030 cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
3031 if(dash) cairo_set_dash (cr, dotted, len, 0.0);
3034 void DrawSeekAxis( int x, int y, int xTo, int yTo )
3039 cr = cairo_create (csBoardWindow);
3041 cairo_move_to (cr, x, y);
3042 cairo_line_to(cr, xTo, yTo );
3044 SetPen(cr, 2, "#000000", 0);
3051 void DrawSeekBackground( int left, int top, int right, int bottom )
3053 cairo_t *cr = cairo_create (csBoardWindow);
3055 cairo_rectangle (cr, left, top, right-left, bottom-top);
3057 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
3064 void DrawSeekText(char *buf, int x, int y)
3066 cairo_t *cr = cairo_create (csBoardWindow);
3068 cairo_select_font_face (cr, "Sans",
3069 CAIRO_FONT_SLANT_NORMAL,
3070 CAIRO_FONT_WEIGHT_NORMAL);
3072 cairo_set_font_size (cr, 12.0);
3074 cairo_move_to (cr, x, y+4);
3075 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
3076 cairo_show_text( cr, buf);
3082 void DrawSeekDot(int x, int y, int colorNr)
3084 cairo_t *cr = cairo_create (csBoardWindow);
3085 int square = colorNr & 0x80;
3089 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3091 cairo_arc(cr, x, y, squareSize/8, 0.0, 2*M_PI);
3093 SetPen(cr, 2, "#000000", 0);
3094 cairo_stroke_preserve(cr);
3096 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
3097 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
3098 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
3109 int boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3110 int boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3111 if(!csBoardWindow) {
3112 csBoardWindow = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), boardWidth, boardHeight);
3113 csBoardBackup = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, boardWidth, boardHeight);
3123 DoDrawGrid(cairo_surface_t *cs)
3125 /* draws a grid starting around Nx, Ny squares starting at x,y */
3131 cr = cairo_create (cs);
3133 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
3134 SetPen(cr, lineGap, "#000000", 0);
3137 for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
3139 cairo_move_to (cr, gridSegments[i].x1, gridSegments[i].y1);
3140 cairo_line_to (cr, gridSegments[i].x2, gridSegments[i].y2);
3153 DoDrawGrid(csBoardWindow);
3154 DoDrawGrid(csBoardBackup);
3158 * event handler for redrawing the board
3161 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3163 DrawPosition(True, NULL);
3168 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3169 { // [HGM] pv: walk PV
3170 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3173 static int savedIndex; /* gross that this is global */
3176 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3179 XawTextPosition index, dummy;
3182 XawTextGetSelectionPos(w, &index, &dummy);
3183 XtSetArg(arg, XtNstring, &val);
3184 XtGetValues(w, &arg, 1);
3185 ReplaceComment(savedIndex, val);
3186 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3187 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3191 EditCommentPopUp (int index, char *title, char *text)
3194 if (text == NULL) text = "";
3195 NewCommentPopup(title, text, index);
3199 CommentPopUp (char *title, char *text)
3201 savedIndex = currentMove; // [HGM] vari
3202 NewCommentPopup(title, text, currentMove);
3208 PopDown(CommentDlg);
3212 /* Disable all user input other than deleting the window */
3213 static int frozen = 0;
3219 /* Grab by a widget that doesn't accept input */
3220 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
3224 /* Undo a FreezeUI */
3228 if (!frozen) return;
3229 XtRemoveGrab(optList[W_MESSG].handle);
3237 static int oldPausing = FALSE;
3238 static GameMode oldmode = (GameMode) -1;
3241 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3243 if (pausing != oldPausing) {
3244 oldPausing = pausing;
3245 MarkMenuItem("Mode.Pause", pausing);
3247 if (appData.showButtonBar) {
3248 /* Always toggle, don't set. Previous code messes up when
3249 invoked while the button is pressed, as releasing it
3250 toggles the state again. */
3253 XtSetArg(args[0], XtNbackground, &oldbg);
3254 XtSetArg(args[1], XtNforeground, &oldfg);
3255 XtGetValues(optList[W_PAUSE].handle,
3257 XtSetArg(args[0], XtNbackground, oldfg);
3258 XtSetArg(args[1], XtNforeground, oldbg);
3260 XtSetValues(optList[W_PAUSE].handle, args, 2);
3264 wname = ModeToWidgetName(oldmode);
3265 if (wname != NULL) {
3266 MarkMenuItem(wname, False);
3268 wname = ModeToWidgetName(gameMode);
3269 if (wname != NULL) {
3270 MarkMenuItem(wname, True);
3273 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
3275 /* Maybe all the enables should be handled here, not just this one */
3276 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
3278 DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
3283 * Button/menu procedures
3286 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3287 char *selected_fen_position=NULL;
3290 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3291 Atom *type_return, XtPointer *value_return,
3292 unsigned long *length_return, int *format_return)
3294 char *selection_tmp;
3296 // if (!selected_fen_position) return False; /* should never happen */
3297 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3298 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3299 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3302 if (f == NULL) return False;
3306 selection_tmp = XtMalloc(len + 1);
3307 count = fread(selection_tmp, 1, len, f);
3310 XtFree(selection_tmp);
3313 selection_tmp[len] = NULLCHAR;
3315 /* note: since no XtSelectionDoneProc was registered, Xt will
3316 * automatically call XtFree on the value returned. So have to
3317 * make a copy of it allocated with XtMalloc */
3318 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3319 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3322 *value_return=selection_tmp;
3323 *length_return=strlen(selection_tmp);
3324 *type_return=*target;
3325 *format_return = 8; /* bits per byte */
3327 } else if (*target == XA_TARGETS(xDisplay)) {
3328 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3329 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3330 targets_tmp[1] = XA_STRING;
3331 *value_return = targets_tmp;
3332 *type_return = XA_ATOM;
3335 // This code leads to a read of value_return out of bounds on 64-bit systems.
3336 // Other code which I have seen always sets *format_return to 32 independent of
3337 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3338 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3339 *format_return = 8 * sizeof(Atom);
3340 if (*format_return > 32) {
3341 *length_return *= *format_return / 32;
3342 *format_return = 32;
3345 *format_return = 32;
3353 /* note: when called from menu all parameters are NULL, so no clue what the
3354 * Widget which was clicked on was, or what the click event was
3357 CopySomething (char *src)
3359 selected_fen_position = src;
3361 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3362 * have a notion of a position that is selected but not copied.
3363 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3365 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3367 SendPositionSelection,
3368 NULL/* lose_ownership_proc */ ,
3369 NULL/* transfer_done_proc */);
3370 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3372 SendPositionSelection,
3373 NULL/* lose_ownership_proc */ ,
3374 NULL/* transfer_done_proc */);
3377 /* function called when the data to Paste is ready */
3379 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3380 Atom *type, XtPointer value, unsigned long *len, int *format)
3383 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3384 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3385 EditPositionPasteFEN(fenstr);
3389 /* called when Paste Position button is pressed,
3390 * all parameters will be NULL */
3392 PastePositionProc ()
3394 XtGetSelectionValue(menuBarWidget,
3395 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3396 /* (XtSelectionCallbackProc) */ PastePositionCB,
3397 NULL, /* client_data passed to PastePositionCB */
3399 /* better to use the time field from the event that triggered the
3400 * call to this function, but that isn't trivial to get
3407 /* note: when called from menu all parameters are NULL, so no clue what the
3408 * Widget which was clicked on was, or what the click event was
3410 /* function called when the data to Paste is ready */
3412 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3413 Atom *type, XtPointer value, unsigned long *len, int *format)
3416 if (value == NULL || *len == 0) {
3417 return; /* nothing had been selected to copy */
3419 f = fopen(gamePasteFilename, "w");
3421 DisplayError(_("Can't open temp file"), errno);
3424 fwrite(value, 1, *len, f);
3427 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3430 /* called when Paste Game button is pressed,
3431 * all parameters will be NULL */
3435 XtGetSelectionValue(menuBarWidget,
3436 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3437 /* (XtSelectionCallbackProc) */ PasteGameCB,
3438 NULL, /* client_data passed to PasteGameCB */
3440 /* better to use the time field from the event that triggered the
3441 * call to this function, but that isn't trivial to get
3450 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3457 { // bassic primitive for determining if modifier keys are pressed
3458 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3461 XQueryKeymap(xDisplay,keys);
3462 for(i=0; i<6; i++) {
3464 j = XKeysymToKeycode(xDisplay, codes[i]);
3465 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3471 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3475 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3476 if ( n == 1 && *buf >= 32 // printable
3477 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3478 ) BoxAutoPopUp (buf);
3482 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3483 { // [HGM] input: let up-arrow recall previous line from history
3488 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3489 { // [HGM] input: let down-arrow recall next line from history
3494 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3500 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3502 if (!TempBackwardActive) {
3503 TempBackwardActive = True;
3509 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3511 /* Check to see if triggered by a key release event for a repeating key.
3512 * If so the next queued event will be a key press of the same key at the same time */
3513 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3515 XPeekEvent(xDisplay, &next);
3516 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3517 next.xkey.keycode == event->xkey.keycode)
3521 TempBackwardActive = False;
3525 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3526 { // called as key binding
3529 if (nprms && *nprms > 0)
3533 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3539 { // called from menu
3540 ManInner(NULL, NULL, NULL, NULL);
3544 SetWindowTitle (char *text, char *title, char *icon)
3548 if (appData.titleInWindow) {
3550 XtSetArg(args[i], XtNlabel, text); i++;
3551 XtSetValues(titleWidget, args, i);
3554 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3555 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3556 XtSetValues(shellWidget, args, i);
3557 XSync(xDisplay, False);
3562 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3568 DisplayIcsInteractionTitle (String message)
3570 if (oldICSInteractionTitle == NULL) {
3571 /* Magic to find the old window title, adapted from vim */
3572 char *wina = getenv("WINDOWID");
3574 Window win = (Window) atoi(wina);
3575 Window root, parent, *children;
3576 unsigned int nchildren;
3577 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3579 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3580 if (!XQueryTree(xDisplay, win, &root, &parent,
3581 &children, &nchildren)) break;
3582 if (children) XFree((void *)children);
3583 if (parent == root || parent == 0) break;
3586 XSetErrorHandler(oldHandler);
3588 if (oldICSInteractionTitle == NULL) {
3589 oldICSInteractionTitle = "xterm";
3592 printf("\033]0;%s\007", message);
3597 XtIntervalId delayedEventTimerXID = 0;
3598 DelayedEventCallback delayedEventCallback = 0;
3603 delayedEventTimerXID = 0;
3604 delayedEventCallback();
3608 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3610 if(delayedEventTimerXID && delayedEventCallback == cb)
3611 // [HGM] alive: replace, rather than add or flush identical event
3612 XtRemoveTimeOut(delayedEventTimerXID);
3613 delayedEventCallback = cb;
3614 delayedEventTimerXID =
3615 XtAppAddTimeOut(appContext, millisec,
3616 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3619 DelayedEventCallback
3622 if (delayedEventTimerXID) {
3623 return delayedEventCallback;
3630 CancelDelayedEvent ()
3632 if (delayedEventTimerXID) {
3633 XtRemoveTimeOut(delayedEventTimerXID);
3634 delayedEventTimerXID = 0;
3638 XtIntervalId loadGameTimerXID = 0;
3641 LoadGameTimerRunning ()
3643 return loadGameTimerXID != 0;
3647 StopLoadGameTimer ()
3649 if (loadGameTimerXID != 0) {
3650 XtRemoveTimeOut(loadGameTimerXID);
3651 loadGameTimerXID = 0;
3659 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3661 loadGameTimerXID = 0;
3666 StartLoadGameTimer (long millisec)
3669 XtAppAddTimeOut(appContext, millisec,
3670 (XtTimerCallbackProc) LoadGameTimerCallback,
3674 XtIntervalId analysisClockXID = 0;
3677 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3679 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3680 || appData.icsEngineAnalyze) { // [DM]
3681 AnalysisPeriodicEvent(0);
3682 StartAnalysisClock();
3687 StartAnalysisClock ()
3690 XtAppAddTimeOut(appContext, 2000,
3691 (XtTimerCallbackProc) AnalysisClockCallback,
3695 XtIntervalId clockTimerXID = 0;
3698 ClockTimerRunning ()
3700 return clockTimerXID != 0;
3706 if (clockTimerXID != 0) {
3707 XtRemoveTimeOut(clockTimerXID);
3716 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3723 StartClockTimer (long millisec)
3726 XtAppAddTimeOut(appContext, millisec,
3727 (XtTimerCallbackProc) ClockTimerCallback,
3732 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3736 Widget w = (Widget) opt->handle;
3738 /* check for low time warning */
3739 Pixel foregroundOrWarningColor = timerForegroundPixel;
3742 appData.lowTimeWarning &&
3743 (timer / 1000) < appData.icsAlarmTime)
3744 foregroundOrWarningColor = lowTimeWarningColor;
3746 if (appData.clockMode) {
3747 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
3748 XtSetArg(args[0], XtNlabel, buf);
3750 snprintf(buf, MSG_SIZ, "%s ", color);
3751 XtSetArg(args[0], XtNlabel, buf);
3756 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3757 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3759 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3760 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3763 XtSetValues(w, args, 3);
3766 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3769 SetClockIcon (int color)
3772 Pixmap pm = *clockIcons[color];
3773 if (iconPixmap != pm) {
3775 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3776 XtSetValues(shellWidget, args, 1);
3781 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3783 InputSource *is = (InputSource *) closure;
3788 if (is->lineByLine) {
3789 count = read(is->fd, is->unused,
3790 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3792 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3795 is->unused += count;
3797 while (p < is->unused) {
3798 q = memchr(p, '\n', is->unused - p);
3799 if (q == NULL) break;
3801 (is->func)(is, is->closure, p, q - p, 0);
3805 while (p < is->unused) {
3810 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3815 (is->func)(is, is->closure, is->buf, count, error);
3820 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3823 ChildProc *cp = (ChildProc *) pr;
3825 is = (InputSource *) calloc(1, sizeof(InputSource));
3826 is->lineByLine = lineByLine;
3830 is->fd = fileno(stdin);
3832 is->kind = cp->kind;
3833 is->fd = cp->fdFrom;
3836 is->unused = is->buf;
3839 is->xid = XtAppAddInput(appContext, is->fd,
3840 (XtPointer) (XtInputReadMask),
3841 (XtInputCallbackProc) DoInputCallback,
3843 is->closure = closure;
3844 return (InputSourceRef) is;
3848 RemoveInputSource (InputSourceRef isr)
3850 InputSource *is = (InputSource *) isr;
3852 if (is->xid == 0) return;
3853 XtRemoveInput(is->xid);
3857 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3859 /* Masks for XPM pieces. Black and white pieces can have
3860 different shapes, but in the interest of retaining my
3861 sanity pieces must have the same outline on both light
3862 and dark squares, and all pieces must use the same
3863 background square colors/images. */
3865 static int xpmDone = 0;
3866 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3867 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3868 static cairo_surface_t *c_animBufs[3*NrOfAnims]; // newBuf, saveBuf
3871 CreateAnimMasks (int pieceDepth)
3877 unsigned long plane;
3880 /* Need a bitmap just to get a GC with right depth */
3881 buf = XCreatePixmap(xDisplay, xBoardWindow,
3883 values.foreground = 1;
3884 values.background = 0;
3885 /* Don't use XtGetGC, not read only */
3886 maskGC = XCreateGC(xDisplay, buf,
3887 GCForeground | GCBackground, &values);
3888 XFreePixmap(xDisplay, buf);
3890 buf = XCreatePixmap(xDisplay, xBoardWindow,
3891 squareSize, squareSize, pieceDepth);
3892 values.foreground = XBlackPixel(xDisplay, xScreen);
3893 values.background = XWhitePixel(xDisplay, xScreen);
3894 bufGC = XCreateGC(xDisplay, buf,
3895 GCForeground | GCBackground, &values);
3897 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3898 /* Begin with empty mask */
3899 if(!xpmDone) // [HGM] pieces: keep using existing
3900 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3901 squareSize, squareSize, 1);
3902 XSetFunction(xDisplay, maskGC, GXclear);
3903 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3904 0, 0, squareSize, squareSize);
3906 /* Take a copy of the piece */
3911 XSetFunction(xDisplay, bufGC, GXcopy);
3912 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3914 0, 0, squareSize, squareSize, 0, 0);
3916 /* XOR the background (light) over the piece */
3917 XSetFunction(xDisplay, bufGC, GXxor);
3919 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3920 0, 0, squareSize, squareSize, 0, 0);
3922 XSetForeground(xDisplay, bufGC, lightSquareColor);
3923 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3926 /* We now have an inverted piece image with the background
3927 erased. Construct mask by just selecting all the non-zero
3928 pixels - no need to reconstruct the original image. */
3929 XSetFunction(xDisplay, maskGC, GXor);
3931 /* Might be quicker to download an XImage and create bitmap
3932 data from it rather than this N copies per piece, but it
3933 only takes a fraction of a second and there is a much
3934 longer delay for loading the pieces. */
3935 for (n = 0; n < pieceDepth; n ++) {
3936 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3937 0, 0, squareSize, squareSize,
3943 XFreePixmap(xDisplay, buf);
3944 XFreeGC(xDisplay, bufGC);
3945 XFreeGC(xDisplay, maskGC);
3949 InitAnimState (AnimNr anr, XWindowAttributes *info)
3955 DrawSeekOpen(); // set cs to board widget
3956 if(c_animBufs[anr]) cairo_surface_destroy (c_animBufs[anr]);
3957 if(c_animBufs[anr+2]) cairo_surface_destroy (c_animBufs[anr+2]);
3958 c_animBufs[anr+4] = csBoardWindow;
3959 c_animBufs[anr+2] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3960 c_animBufs[anr] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3963 /* Each buffer is square size, same depth as window */
3964 animBufs[anr+4] = xBoardWindow;
3965 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3966 squareSize, squareSize, info->depth);
3967 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3968 squareSize, squareSize, info->depth);
3970 /* Create a plain GC for blitting */
3971 mask = GCForeground | GCBackground | GCFunction |
3972 GCPlaneMask | GCGraphicsExposures;
3973 values.foreground = XBlackPixel(xDisplay, xScreen);
3974 values.background = XWhitePixel(xDisplay, xScreen);
3975 values.function = GXcopy;
3976 values.plane_mask = AllPlanes;
3977 values.graphics_exposures = False;
3978 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3980 /* Piece will be copied from an existing context at
3981 the start of each new animation/drag. */
3982 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3984 /* Outline will be a read-only copy of an existing */
3985 animGCs[anr+4] = None;
3991 XWindowAttributes info;
3993 if (!cairoAnimate && xpmDone && gameInfo.variant == oldVariant) return;
3994 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3995 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3997 InitAnimState(Game, &info);
3998 InitAnimState(Player, &info);
4000 /* For XPM pieces, we need bitmaps to use as masks. */
4001 if (useImages & !xpmDone)
4002 CreateAnimMasks(info.depth), xpmDone = 1;
4007 static Boolean frameWaiting;
4010 FrameAlarm (int sig)
4012 frameWaiting = False;
4013 /* In case System-V style signals. Needed?? */
4014 signal(SIGALRM, FrameAlarm);
4018 FrameDelay (int time)
4020 struct itimerval delay;
4022 XSync(xDisplay, False);
4025 frameWaiting = True;
4026 signal(SIGALRM, FrameAlarm);
4027 delay.it_interval.tv_sec =
4028 delay.it_value.tv_sec = time / 1000;
4029 delay.it_interval.tv_usec =
4030 delay.it_value.tv_usec = (time % 1000) * 1000;
4031 setitimer(ITIMER_REAL, &delay, NULL);
4032 while (frameWaiting) pause();
4033 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
4034 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
4035 setitimer(ITIMER_REAL, &delay, NULL);
4042 FrameDelay (int time)
4044 XSync(xDisplay, False);
4046 usleep(time * 1000);
4052 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
4056 /* Bitmap for piece being moved. */
4057 if (appData.monoMode) {
4058 *mask = *pieceToSolid(piece);
4059 } else if (useImages) {
4061 *mask = xpmMask[piece];
4063 *mask = ximMaskPm[piece];
4066 *mask = *pieceToSolid(piece);
4069 /* GC for piece being moved. Square color doesn't matter, but
4070 since it gets modified we make a copy of the original. */
4072 if (appData.monoMode)
4077 if (appData.monoMode)
4082 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
4084 /* Outline only used in mono mode and is not modified */
4086 *outline = bwPieceGC;
4088 *outline = wbPieceGC;
4092 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
4097 /* Draw solid rectangle which will be clipped to shape of piece */
4098 XFillRectangle(xDisplay, dest, clip,
4099 0, 0, squareSize, squareSize);
4100 if (appData.monoMode)
4101 /* Also draw outline in contrasting color for black
4102 on black / white on white cases */
4103 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
4104 0, 0, squareSize, squareSize, 0, 0, 1);
4106 /* Copy the piece */
4111 if(appData.upsideDown && flipView) kind ^= 2;
4112 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4114 0, 0, squareSize, squareSize,
4120 CairoOverlayPiece (ChessSquare piece, cairo_surface_t *dest)
4122 static ChessSquare oldPiece = -1;
4124 static cairo_t *pieceSource;
4125 extern int doubleClick; // in backend.c
4126 if(piece != oldPiece || squareSize != oldSize) { // try make it faster by only changing cr if we need other piece
4127 if(pieceSource) cairo_destroy (pieceSource);
4128 pieceSource = cairo_create (dest);
4129 cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
4130 oldPiece = piece; oldSize = squareSize;
4132 if(doubleClick) cairo_paint_with_alpha (pieceSource, 0.6);
4133 else cairo_paint(pieceSource);
4137 InsertPiece (AnimNr anr, ChessSquare piece)
4140 CairoOverlayPiece(piece, c_animBufs[anr]);
4142 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
4146 DrawBlank (AnimNr anr, int x, int y, int startColor)
4149 BlankSquare(x, y, startColor, EmptySquare, (Drawable) c_animBufs[anr+2], 0);
4151 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
4154 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
4155 int srcX, int srcY, int width, int height, int destX, int destY)
4158 cairo_t *cr;// = cairo_create (c_animBufs[anr+destBuf]);
4159 cr = cairo_create (c_animBufs[anr+destBuf]);
4160 if(c_animBufs[anr+srcBuf] == csBoardWindow)
4161 cairo_set_source_surface (cr, csBoardBackup, destX - srcX, destY - srcY);
4163 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
4164 cairo_rectangle (cr, destX, destY, width, height);
4167 if(c_animBufs[anr+destBuf] == csBoardWindow) {
4168 cr = cairo_create (csBoardBackup); // also draw to backup
4169 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
4170 cairo_rectangle (cr, destX, destY, width, height);
4175 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
4176 srcX, srcY, width, height, destX, destY);
4180 SetDragPiece (AnimNr anr, ChessSquare piece)
4183 if(cairoAnimate) return;
4184 /* The piece will be drawn using its own bitmap as a matte */
4185 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
4186 XSetClipMask(xDisplay, animGCs[anr+2], mask);
4189 /* [AS] Arrow highlighting support */
4191 void DrawPolygon(Pnt arrow[], int nr)
4192 { // for now on own surface; eventually this should become a global that is only destroyed on resize
4193 cairo_surface_t *boardSurface;
4196 int w = lineGap + BOARD_WIDTH * (squareSize + lineGap);
4197 int h = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
4198 boardSurface = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), w, h);
4199 cr = cairo_create (boardSurface);
4200 cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
4201 for (i=0;i<nr;i++) {
4202 cairo_line_to(cr, arrow[i].x, arrow[i].y);
4204 if(appData.monoMode) { // should we always outline arrow?
4205 cairo_line_to(cr, arrow[0].x, arrow[0].y);
4206 SetPen(cr, 2, "#000000", 0);
4207 cairo_stroke_preserve(cr);
4209 SetPen(cr, 2, appData.highlightSquareColor, 0);
4214 cairo_surface_destroy (boardSurface);
4218 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
4220 char buf[MSG_SIZ], *logoName = buf;
4221 if(appData.logo[n][0]) {
4222 logoName = appData.logo[n];
4223 } else if(appData.autoLogo) {
4224 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
4225 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
4226 } else if(appData.directory[n] && appData.directory[n][0]) {
4227 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
4231 { ASSIGN(cps->programLogo, logoName); }
4235 UpdateLogos (int displ)
4237 if(optList[W_WHITE-1].handle == NULL) return;
4238 LoadLogo(&first, 0, 0);
4239 LoadLogo(&second, 1, appData.icsActive);
4240 if(displ) DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);