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 = True;
344 static cairo_surface_t *csBoardWindow;
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 if(!dual) dual = XtWindow(dualOptions[3].handle); // must be first call
898 xBoardWindow = dual; // swap them
903 PopUpStartupDialog ()
904 { // start menu not implemented in XBoard
908 ConvertToLine (int argc, char **argv)
910 static char line[128*1024], buf[1024];
914 for(i=1; i<argc; i++)
916 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
917 && argv[i][0] != '{' )
918 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
920 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
921 strncat(line, buf, 128*1024 - strlen(line) - 1 );
924 line[strlen(line)-1] = NULLCHAR;
928 //--------------------------------------------------------------------------------------------
930 #define BoardSize int
932 InitDrawingSizes (BoardSize boardSize, int flags)
933 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
934 Dimension boardWidth, boardHeight, w, h;
936 static Dimension oldWidth, oldHeight;
937 static VariantClass oldVariant;
938 static int oldMono = -1, oldTwoBoards = 0;
940 if(!formWidget) return;
942 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
943 oldTwoBoards = twoBoards;
945 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
946 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
947 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
949 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
951 oldWidth = boardWidth; oldHeight = boardHeight;
955 * Inhibit shell resizing.
957 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW ;
958 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
959 shellArgs[4].value = shellArgs[2].value = w;
960 shellArgs[5].value = shellArgs[3].value = h;
961 XtSetValues(shellWidget, &shellArgs[0], 6);
963 XSync(xDisplay, False);
967 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
970 if(gameInfo.variant != oldVariant) { // and only if variant changed
975 for(p=0; p<=(int)WhiteKing; p++)
976 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
977 if(gameInfo.variant == VariantShogi) {
978 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
979 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
980 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
981 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
982 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
985 if(gameInfo.variant == VariantGothic) {
986 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
989 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
990 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
991 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
994 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
995 for(p=0; p<=(int)WhiteKing; p++)
996 ximMaskPm[p] = ximMaskPm2[p]; // defaults
997 if(gameInfo.variant == VariantShogi) {
998 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
999 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1000 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1001 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1002 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1005 if(gameInfo.variant == VariantGothic) {
1006 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1009 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1010 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1011 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1016 for(i=0; i<2; i++) {
1018 for(p=0; p<=(int)WhiteKing; p++)
1019 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1020 if(gameInfo.variant == VariantShogi) {
1021 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1022 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1023 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1024 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1025 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1028 if(gameInfo.variant == VariantGothic) {
1029 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1032 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1033 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1034 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1038 for(i=0; i<2; i++) {
1040 printf("Copy pieces\n");
1041 for(p=0; p<=(int)WhiteKing; p++)
1042 pngPieceBitmaps[i][p] = pngPieceBitmaps2[i][p]; // defaults
1044 oldMono = -10; // kludge to force recreation of animation masks
1045 oldVariant = gameInfo.variant;
1048 if(appData.monoMode != oldMono)
1051 oldMono = appData.monoMode;
1055 MakeOneColor (char *name, Pixel *color)
1057 XrmValue vFrom, vTo;
1058 if (!appData.monoMode) {
1059 vFrom.addr = (caddr_t) name;
1060 vFrom.size = strlen(name);
1061 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1062 if (vTo.addr == NULL) {
1063 appData.monoMode = True;
1066 *color = *(Pixel *) vTo.addr;
1074 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1075 int forceMono = False;
1077 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1078 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1079 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1080 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1081 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1082 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1083 if (appData.lowTimeWarning)
1084 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1085 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1086 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1093 { // [HGM] taken out of main
1095 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1096 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1097 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1099 if (appData.bitmapDirectory[0] != NULLCHAR) {
1103 CreateXPMBoard(appData.liteBackTextureFile, 1);
1104 CreateXPMBoard(appData.darkBackTextureFile, 0);
1106 if (appData.pngDirectory[0] != NULLCHAR) { // for now do in parallel
1111 /* Create regular pieces */
1112 if (!useImages) CreatePieces();
1117 InitDrawingParams ()
1119 MakeColors(); CreateGCs(True);
1124 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1125 { // detervtomine what fonts to use, and create them
1129 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1130 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1131 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1132 appData.font = fontTable[MESSAGE_FONT][squareSize];
1133 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1134 appData.coordFont = fontTable[COORD_FONT][squareSize];
1137 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1138 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1139 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1140 fontSet = CreateFontSet(appData.font);
1141 clockFontSet = CreateFontSet(appData.clockFont);
1143 /* For the coordFont, use the 0th font of the fontset. */
1144 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1145 XFontStruct **font_struct_list;
1146 XFontSetExtents *fontSize;
1147 char **font_name_list;
1148 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1149 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1150 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1151 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1152 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1155 appData.font = FindFont(appData.font, fontPxlSize);
1156 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1157 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1158 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1159 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1160 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1161 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1162 // textHeight in !NLS mode!
1164 countFontID = coordFontID; // [HGM] holdings
1165 countFontStruct = coordFontStruct;
1167 xdb = XtDatabase(xDisplay);
1169 XrmPutLineResource(&xdb, "*international: True");
1170 vTo.size = sizeof(XFontSet);
1171 vTo.addr = (XtPointer) &fontSet;
1172 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1174 XrmPutStringResource(&xdb, "*font", appData.font);
1179 PrintArg (ArgType t)
1184 case ArgInt: p = " N"; break;
1185 case ArgString: p = " STR"; break;
1186 case ArgBoolean: p = " TF"; break;
1187 case ArgSettingsFilename:
1188 case ArgFilename: p = " FILE"; break;
1189 case ArgX: p = " Nx"; break;
1190 case ArgY: p = " Ny"; break;
1191 case ArgAttribs: p = " TEXTCOL"; break;
1192 case ArgColor: p = " COL"; break;
1193 case ArgFont: p = " FONT"; break;
1194 case ArgBoardSize: p = " SIZE"; break;
1195 case ArgFloat: p = " FLOAT"; break;
1200 case ArgCommSettings:
1211 ArgDescriptor *q, *p = argDescriptors+5;
1212 printf("\nXBoard accepts the following options:\n"
1213 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1214 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1215 " SIZE = board-size spec(s)\n"
1216 " Within parentheses are short forms, or options to set to true or false.\n"
1217 " Persistent options (saved in the settings file) are marked with *)\n\n");
1219 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1220 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1221 if(p->save) strcat(buf+len, "*");
1222 for(q=p+1; q->argLoc == p->argLoc; q++) {
1223 if(q->argName[0] == '-') continue;
1224 strcat(buf+len, q == p+1 ? " (" : " ");
1225 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1227 if(q != p+1) strcat(buf+len, ")");
1229 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1232 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1236 main (int argc, char **argv)
1238 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1239 XSetWindowAttributes window_attributes;
1241 Dimension boardWidth, boardHeight, w, h;
1243 int forceMono = False;
1245 srandom(time(0)); // [HGM] book: make random truly random
1247 setbuf(stdout, NULL);
1248 setbuf(stderr, NULL);
1251 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1252 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1256 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1261 programName = strrchr(argv[0], '/');
1262 if (programName == NULL)
1263 programName = argv[0];
1268 XtSetLanguageProc(NULL, NULL, NULL);
1269 if (appData.debugMode) {
1270 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1273 bindtextdomain(PACKAGE, LOCALEDIR);
1274 textdomain(PACKAGE);
1277 appData.boardSize = "";
1278 InitAppData(ConvertToLine(argc, argv));
1280 if (p == NULL) p = "/tmp";
1281 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1282 gameCopyFilename = (char*) malloc(i);
1283 gamePasteFilename = (char*) malloc(i);
1284 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1285 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1287 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1288 static char buf[MSG_SIZ];
1289 EscapeExpand(buf, appData.firstInitString);
1290 appData.firstInitString = strdup(buf);
1291 EscapeExpand(buf, appData.secondInitString);
1292 appData.secondInitString = strdup(buf);
1293 EscapeExpand(buf, appData.firstComputerString);
1294 appData.firstComputerString = strdup(buf);
1295 EscapeExpand(buf, appData.secondComputerString);
1296 appData.secondComputerString = strdup(buf);
1299 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1302 if (chdir(chessDir) != 0) {
1303 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1309 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1310 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1311 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1312 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1315 setbuf(debugFP, NULL);
1318 /* [HGM,HR] make sure board size is acceptable */
1319 if(appData.NrFiles > BOARD_FILES ||
1320 appData.NrRanks > BOARD_RANKS )
1321 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1324 /* This feature does not work; animation needs a rewrite */
1325 appData.highlightDragging = FALSE;
1329 gameInfo.variant = StringToVariant(appData.variant);
1330 InitPosition(FALSE);
1333 XtAppInitialize(&appContext, "XBoard", shellOptions,
1334 XtNumber(shellOptions),
1335 &argc, argv, xboardResources, NULL, 0);
1337 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1338 clientResources, XtNumber(clientResources),
1341 xDisplay = XtDisplay(shellWidget);
1342 xScreen = DefaultScreen(xDisplay);
1343 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1346 * determine size, based on supplied or remembered -size, or screen size
1348 if (isdigit(appData.boardSize[0])) {
1349 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1350 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1351 &fontPxlSize, &smallLayout, &tinyLayout);
1353 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1354 programName, appData.boardSize);
1358 /* Find some defaults; use the nearest known size */
1359 SizeDefaults *szd, *nearest;
1360 int distance = 99999;
1361 nearest = szd = sizeDefaults;
1362 while (szd->name != NULL) {
1363 if (abs(szd->squareSize - squareSize) < distance) {
1365 distance = abs(szd->squareSize - squareSize);
1366 if (distance == 0) break;
1370 if (i < 2) lineGap = nearest->lineGap;
1371 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1372 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1373 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1374 if (i < 6) smallLayout = nearest->smallLayout;
1375 if (i < 7) tinyLayout = nearest->tinyLayout;
1378 SizeDefaults *szd = sizeDefaults;
1379 if (*appData.boardSize == NULLCHAR) {
1380 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1381 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1384 if (szd->name == NULL) szd--;
1385 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1387 while (szd->name != NULL &&
1388 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1389 if (szd->name == NULL) {
1390 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1391 programName, appData.boardSize);
1395 squareSize = szd->squareSize;
1396 lineGap = szd->lineGap;
1397 clockFontPxlSize = szd->clockFontPxlSize;
1398 coordFontPxlSize = szd->coordFontPxlSize;
1399 fontPxlSize = szd->fontPxlSize;
1400 smallLayout = szd->smallLayout;
1401 tinyLayout = szd->tinyLayout;
1402 // [HGM] font: use defaults from settings file if available and not overruled
1405 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1406 if (strlen(appData.pixmapDirectory) > 0) {
1407 p = ExpandPathName(appData.pixmapDirectory);
1409 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1410 appData.pixmapDirectory);
1413 if (appData.debugMode) {
1414 fprintf(stderr, _("\
1415 XBoard square size (hint): %d\n\
1416 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1418 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1419 if (appData.debugMode) {
1420 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1423 defaultLineGap = lineGap;
1424 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1426 /* [HR] height treated separately (hacked) */
1427 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1428 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1431 * Determine what fonts to use.
1433 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1436 * Detect if there are not enough colors available and adapt.
1438 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1439 appData.monoMode = True;
1442 forceMono = MakeColors();
1445 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1447 appData.monoMode = True;
1450 if (appData.monoMode && appData.debugMode) {
1451 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1452 (unsigned long) XWhitePixel(xDisplay, xScreen),
1453 (unsigned long) XBlackPixel(xDisplay, xScreen));
1456 ParseIcsTextColors();
1458 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1464 layoutName = "tinyLayout";
1465 } else if (smallLayout) {
1466 layoutName = "smallLayout";
1468 layoutName = "normalLayout";
1471 optList = BoardPopUp(squareSize, lineGap, (void*)
1477 boardWidget = optList[W_BOARD].handle;
1478 menuBarWidget = optList[W_MENU].handle;
1479 dropMenu = optList[W_DROP].handle;
1480 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1481 formWidget = XtParent(boardWidget);
1482 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1483 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1484 XtGetValues(optList[W_WHITE].handle, args, 2);
1485 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1486 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1487 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1488 XtGetValues(optList[W_PAUSE].handle, args, 2);
1490 AppendEnginesToMenu(appData.recentEngineList);
1492 xBoardWindow = XtWindow(boardWidget);
1494 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1495 // not need to go into InitDrawingSizes().
1498 * Create X checkmark bitmap and initialize option menu checks.
1500 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1501 checkmark_bits, checkmark_width, checkmark_height);
1507 ReadBitmap(&wIconPixmap, "icon_white.bm",
1508 icon_white_bits, icon_white_width, icon_white_height);
1509 ReadBitmap(&bIconPixmap, "icon_black.bm",
1510 icon_black_bits, icon_black_width, icon_black_height);
1511 iconPixmap = wIconPixmap;
1513 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1514 XtSetValues(shellWidget, args, i);
1517 * Create a cursor for the board widget.
1519 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1520 XChangeWindowAttributes(xDisplay, xBoardWindow,
1521 CWCursor, &window_attributes);
1524 * Inhibit shell resizing.
1526 shellArgs[0].value = (XtArgVal) &w;
1527 shellArgs[1].value = (XtArgVal) &h;
1528 XtGetValues(shellWidget, shellArgs, 2);
1529 shellArgs[4].value = shellArgs[2].value = w;
1530 shellArgs[5].value = shellArgs[3].value = h;
1531 XtSetValues(shellWidget, &shellArgs[2], 4);
1532 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1533 marginH = h - boardHeight;
1535 CatchDeleteWindow(shellWidget, "QuitProc");
1541 if(appData.logoSize)
1542 { // locate and read user logo
1544 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1545 ASSIGN(userLogo, buf);
1548 if (appData.animate || appData.animateDragging)
1551 XtAugmentTranslations(formWidget,
1552 XtParseTranslationTable(globalTranslations));
1554 XtAddEventHandler(formWidget, KeyPressMask, False,
1555 (XtEventHandler) MoveTypeInProc, NULL);
1556 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1557 (XtEventHandler) EventProc, NULL);
1559 /* [AS] Restore layout */
1560 if( wpMoveHistory.visible ) {
1564 if( wpEvalGraph.visible )
1569 if( wpEngineOutput.visible ) {
1570 EngineOutputPopUp();
1575 if (errorExitStatus == -1) {
1576 if (appData.icsActive) {
1577 /* We now wait until we see "login:" from the ICS before
1578 sending the logon script (problems with timestamp otherwise) */
1579 /*ICSInitScript();*/
1580 if (appData.icsInputBox) ICSInputBoxPopUp();
1584 signal(SIGWINCH, TermSizeSigHandler);
1586 signal(SIGINT, IntSigHandler);
1587 signal(SIGTERM, IntSigHandler);
1588 if (*appData.cmailGameName != NULLCHAR) {
1589 signal(SIGUSR1, CmailSigHandler);
1593 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1596 // XtSetKeyboardFocus(shellWidget, formWidget);
1597 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1599 XtAppMainLoop(appContext);
1600 if (appData.debugMode) fclose(debugFP); // [DM] debug
1605 TermSizeSigHandler (int sig)
1611 IntSigHandler (int sig)
1617 CmailSigHandler (int sig)
1622 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1624 /* Activate call-back function CmailSigHandlerCallBack() */
1625 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1627 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1631 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1634 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1636 /**** end signal code ****/
1639 #define Abs(n) ((n)<0 ? -(n) : (n))
1643 InsertPxlSize (char *pattern, int targetPxlSize)
1645 char *base_fnt_lst, strInt[12], *p, *q;
1646 int alternatives, i, len, strIntLen;
1649 * Replace the "*" (if present) in the pixel-size slot of each
1650 * alternative with the targetPxlSize.
1654 while ((p = strchr(p, ',')) != NULL) {
1658 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1659 strIntLen = strlen(strInt);
1660 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1664 while (alternatives--) {
1665 char *comma = strchr(p, ',');
1666 for (i=0; i<14; i++) {
1667 char *hyphen = strchr(p, '-');
1669 if (comma && hyphen > comma) break;
1670 len = hyphen + 1 - p;
1671 if (i == 7 && *p == '*' && len == 2) {
1673 memcpy(q, strInt, strIntLen);
1683 len = comma + 1 - p;
1690 return base_fnt_lst;
1694 CreateFontSet (char *base_fnt_lst)
1697 char **missing_list;
1701 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1702 &missing_list, &missing_count, &def_string);
1703 if (appData.debugMode) {
1705 XFontStruct **font_struct_list;
1706 char **font_name_list;
1707 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1709 fprintf(debugFP, " got list %s, locale %s\n",
1710 XBaseFontNameListOfFontSet(fntSet),
1711 XLocaleOfFontSet(fntSet));
1712 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1713 for (i = 0; i < count; i++) {
1714 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1717 for (i = 0; i < missing_count; i++) {
1718 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1721 if (fntSet == NULL) {
1722 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1727 #else // not ENABLE_NLS
1729 * Find a font that matches "pattern" that is as close as
1730 * possible to the targetPxlSize. Prefer fonts that are k
1731 * pixels smaller to fonts that are k pixels larger. The
1732 * pattern must be in the X Consortium standard format,
1733 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1734 * The return value should be freed with XtFree when no
1738 FindFont (char *pattern, int targetPxlSize)
1740 char **fonts, *p, *best, *scalable, *scalableTail;
1741 int i, j, nfonts, minerr, err, pxlSize;
1743 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1745 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1746 programName, pattern);
1753 for (i=0; i<nfonts; i++) {
1756 if (*p != '-') continue;
1758 if (*p == NULLCHAR) break;
1759 if (*p++ == '-') j++;
1761 if (j < 7) continue;
1764 scalable = fonts[i];
1767 err = pxlSize - targetPxlSize;
1768 if (Abs(err) < Abs(minerr) ||
1769 (minerr > 0 && err < 0 && -err == minerr)) {
1775 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1776 /* If the error is too big and there is a scalable font,
1777 use the scalable font. */
1778 int headlen = scalableTail - scalable;
1779 p = (char *) XtMalloc(strlen(scalable) + 10);
1780 while (isdigit(*scalableTail)) scalableTail++;
1781 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1783 p = (char *) XtMalloc(strlen(best) + 2);
1784 safeStrCpy(p, best, strlen(best)+1 );
1786 if (appData.debugMode) {
1787 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1788 pattern, targetPxlSize, p);
1790 XFreeFontNames(fonts);
1797 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1798 // must be called before all non-first callse to CreateGCs()
1799 XtReleaseGC(shellWidget, highlineGC);
1800 XtReleaseGC(shellWidget, lightSquareGC);
1801 XtReleaseGC(shellWidget, darkSquareGC);
1802 XtReleaseGC(shellWidget, lineGC);
1803 if (appData.monoMode) {
1804 if (DefaultDepth(xDisplay, xScreen) == 1) {
1805 XtReleaseGC(shellWidget, wbPieceGC);
1807 XtReleaseGC(shellWidget, bwPieceGC);
1810 XtReleaseGC(shellWidget, prelineGC);
1811 XtReleaseGC(shellWidget, wdPieceGC);
1812 XtReleaseGC(shellWidget, wlPieceGC);
1813 XtReleaseGC(shellWidget, bdPieceGC);
1814 XtReleaseGC(shellWidget, blPieceGC);
1819 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1821 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1822 | GCBackground | GCFunction | GCPlaneMask;
1823 gc_values->foreground = foreground;
1824 gc_values->background = background;
1825 return XtGetGC(shellWidget, value_mask, gc_values);
1829 CreateGCs (int redo)
1831 XGCValues gc_values;
1833 Pixel white = XWhitePixel(xDisplay, xScreen);
1834 Pixel black = XBlackPixel(xDisplay, xScreen);
1836 gc_values.plane_mask = AllPlanes;
1837 gc_values.line_width = lineGap;
1838 gc_values.line_style = LineSolid;
1839 gc_values.function = GXcopy;
1842 DeleteGCs(); // called a second time; clean up old GCs first
1843 } else { // [HGM] grid and font GCs created on first call only
1844 coordGC = CreateOneGC(&gc_values, black, white);
1845 XSetFont(xDisplay, coordGC, coordFontID);
1847 // [HGM] make font for holdings counts (white on black)
1848 countGC = CreateOneGC(&gc_values, white, black);
1849 XSetFont(xDisplay, countGC, countFontID);
1851 lineGC = CreateOneGC(&gc_values, black, black);
1853 if (appData.monoMode) {
1855 highlineGC = CreateOneGC(&gc_values, white, white);
1856 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1857 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1859 if (DefaultDepth(xDisplay, xScreen) == 1) {
1860 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1861 gc_values.function = GXcopyInverted;
1862 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1863 gc_values.function = GXcopy;
1864 if (XBlackPixel(xDisplay, xScreen) == 1) {
1865 bwPieceGC = darkSquareGC;
1866 wbPieceGC = copyInvertedGC;
1868 bwPieceGC = copyInvertedGC;
1869 wbPieceGC = lightSquareGC;
1874 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1875 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1876 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1877 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1878 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1879 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1880 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1881 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1886 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1894 fp = fopen(filename, "rb");
1896 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1903 for (y=0; y<h; ++y) {
1904 for (x=0; x<h; ++x) {
1909 XPutPixel(xim, x, y, blackPieceColor);
1911 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1914 XPutPixel(xim, x, y, darkSquareColor);
1916 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1919 XPutPixel(xim, x, y, whitePieceColor);
1921 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1924 XPutPixel(xim, x, y, lightSquareColor);
1926 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1934 /* create Pixmap of piece */
1935 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1937 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1940 /* create Pixmap of clipmask
1941 Note: We assume the white/black pieces have the same
1942 outline, so we make only 6 masks. This is okay
1943 since the XPM clipmask routines do the same. */
1945 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1947 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1950 /* now create the 1-bit version */
1951 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1954 values.foreground = 1;
1955 values.background = 0;
1957 /* Don't use XtGetGC, not read only */
1958 maskGC = XCreateGC(xDisplay, *mask,
1959 GCForeground | GCBackground, &values);
1960 XCopyPlane(xDisplay, temp, *mask, maskGC,
1961 0, 0, squareSize, squareSize, 0, 0, 1);
1962 XFreePixmap(xDisplay, temp);
1967 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1975 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1980 /* The XSynchronize calls were copied from CreatePieces.
1981 Not sure if needed, but can't hurt */
1982 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1985 /* temp needed by loadXIM() */
1986 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1987 0, 0, ss, ss, AllPlanes, XYPixmap);
1989 if (strlen(appData.pixmapDirectory) == 0) {
1993 if (appData.monoMode) {
1994 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1998 fprintf(stderr, _("\nLoading XIMs...\n"));
2000 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2001 fprintf(stderr, "%d", piece+1);
2002 for (kind=0; kind<4; kind++) {
2003 fprintf(stderr, ".");
2004 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2005 ExpandPathName(appData.pixmapDirectory),
2006 piece <= (int) WhiteKing ? "" : "w",
2007 pieceBitmapNames[piece],
2009 ximPieceBitmap[kind][piece] =
2010 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2011 0, 0, ss, ss, AllPlanes, XYPixmap);
2012 if (appData.debugMode)
2013 fprintf(stderr, _("(File:%s:) "), buf);
2014 loadXIM(ximPieceBitmap[kind][piece],
2016 &(xpmPieceBitmap2[kind][piece]),
2017 &(ximMaskPm2[piece]));
2018 if(piece <= (int)WhiteKing)
2019 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2021 fprintf(stderr," ");
2023 /* Load light and dark squares */
2024 /* If the LSQ and DSQ pieces don't exist, we will
2025 draw them with solid squares. */
2026 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2027 if (access(buf, 0) != 0) {
2031 fprintf(stderr, _("light square "));
2033 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2034 0, 0, ss, ss, AllPlanes, XYPixmap);
2035 if (appData.debugMode)
2036 fprintf(stderr, _("(File:%s:) "), buf);
2038 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2039 fprintf(stderr, _("dark square "));
2040 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2041 ExpandPathName(appData.pixmapDirectory), ss);
2042 if (appData.debugMode)
2043 fprintf(stderr, _("(File:%s:) "), buf);
2045 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2046 0, 0, ss, ss, AllPlanes, XYPixmap);
2047 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2048 xpmJailSquare = xpmLightSquare;
2050 fprintf(stderr, _("Done.\n"));
2052 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2055 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2059 CreateXPMBoard (char *s, int kind)
2063 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2064 if(strstr(s, ".png")) {
2065 cairo_surface_t *img = cairo_image_surface_create_from_png (s);
2067 useTexture |= kind + 1; pngBoardBitmap[kind] = img;
2068 textureW[kind] = cairo_image_surface_get_width (img);
2069 textureH[kind] = cairo_image_surface_get_height (img);
2072 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2073 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2079 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2080 // thisroutine has to be called t free the old piece pixmaps
2082 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2083 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2085 XFreePixmap(xDisplay, xpmLightSquare);
2086 XFreePixmap(xDisplay, xpmDarkSquare);
2095 u_int ss = squareSize;
2097 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2098 XpmColorSymbol symbols[4];
2099 static int redo = False;
2101 if(redo) FreeXPMPieces(); else redo = 1;
2103 /* The XSynchronize calls were copied from CreatePieces.
2104 Not sure if needed, but can't hurt */
2105 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2107 /* Setup translations so piece colors match square colors */
2108 symbols[0].name = "light_piece";
2109 symbols[0].value = appData.whitePieceColor;
2110 symbols[1].name = "dark_piece";
2111 symbols[1].value = appData.blackPieceColor;
2112 symbols[2].name = "light_square";
2113 symbols[2].value = appData.lightSquareColor;
2114 symbols[3].name = "dark_square";
2115 symbols[3].value = appData.darkSquareColor;
2117 attr.valuemask = XpmColorSymbols;
2118 attr.colorsymbols = symbols;
2119 attr.numsymbols = 4;
2121 if (appData.monoMode) {
2122 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2126 if (strlen(appData.pixmapDirectory) == 0) {
2127 XpmPieces* pieces = builtInXpms;
2130 while (pieces->size != squareSize && pieces->size) pieces++;
2131 if (!pieces->size) {
2132 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2135 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2136 for (kind=0; kind<4; kind++) {
2138 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2139 pieces->xpm[piece][kind],
2140 &(xpmPieceBitmap2[kind][piece]),
2141 NULL, &attr)) != 0) {
2142 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2146 if(piece <= (int) WhiteKing)
2147 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2151 xpmJailSquare = xpmLightSquare;
2155 fprintf(stderr, _("\nLoading XPMs...\n"));
2158 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2159 fprintf(stderr, "%d ", piece+1);
2160 for (kind=0; kind<4; kind++) {
2161 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2162 ExpandPathName(appData.pixmapDirectory),
2163 piece > (int) WhiteKing ? "w" : "",
2164 pieceBitmapNames[piece],
2166 if (appData.debugMode) {
2167 fprintf(stderr, _("(File:%s:) "), buf);
2169 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2170 &(xpmPieceBitmap2[kind][piece]),
2171 NULL, &attr)) != 0) {
2172 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2173 // [HGM] missing: read of unorthodox piece failed; substitute King.
2174 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2175 ExpandPathName(appData.pixmapDirectory),
2177 if (appData.debugMode) {
2178 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2180 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2181 &(xpmPieceBitmap2[kind][piece]),
2185 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2190 if(piece <= (int) WhiteKing)
2191 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2194 /* Load light and dark squares */
2195 /* If the LSQ and DSQ pieces don't exist, we will
2196 draw them with solid squares. */
2197 fprintf(stderr, _("light square "));
2198 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2199 if (access(buf, 0) != 0) {
2203 if (appData.debugMode)
2204 fprintf(stderr, _("(File:%s:) "), buf);
2206 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2207 &xpmLightSquare, NULL, &attr)) != 0) {
2208 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2211 fprintf(stderr, _("dark square "));
2212 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2213 ExpandPathName(appData.pixmapDirectory), ss);
2214 if (appData.debugMode) {
2215 fprintf(stderr, _("(File:%s:) "), buf);
2217 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2218 &xpmDarkSquare, NULL, &attr)) != 0) {
2219 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2223 xpmJailSquare = xpmLightSquare;
2224 fprintf(stderr, _("Done.\n"));
2226 oldVariant = -1; // kludge to force re-makig of animation masks
2227 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2230 #endif /* HAVE_LIBXPM */
2232 char *pngPieceNames[] = // must be in same order as internal piece encoding
2233 { "Pawn", "Knight", "Bishop", "Rook", "Queen", "Advisor", "Elephant", "Archbishop", "Marshall", "Gold", "Commoner",
2234 "Canon", "Nightrider", "CrownedBishop", "CrownedRook", "Princess", "Chancellor", "Hawk", "Lance", "Cobra", "Unicorn", "King",
2235 "GoldKnight", "GoldLance", "GoldPawn", "GoldSilver", NULL
2239 ScaleOnePiece (char *name, int color, int piece)
2243 cairo_surface_t *img, *cs;
2245 static cairo_surface_t *pngPieceImages[2][(int)BlackPawn+4]; // png 256 x 256 images
2247 if(pngPieceImages[color][piece] == NULL) { // if PNG file for this piece was not yet read, read it now and store it
2248 snprintf(buf, MSG_SIZ, "%s/%s%s.png", appData.pngDirectory, color ? "Black" : "White", pngPieceNames[piece]);
2249 pngPieceImages[color][piece] = img = cairo_image_surface_create_from_png (buf);
2250 w = cairo_image_surface_get_width (img);
2251 h = cairo_image_surface_get_height (img);
2252 if(w != 256 || h != 256) { printf("Bad png size %dx%d in %s\n", w, h, buf); exit(1); }
2255 // create new bitmap to hold scaled piece image (and remove any old)
2256 if(pngPieceBitmaps2[color][piece]) cairo_surface_destroy (pngPieceBitmaps2[color][piece]);
2257 pngPieceBitmaps2[color][piece] = cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
2258 if(piece <= WhiteKing) pngPieceBitmaps[color][piece] = cs;
2259 // scaled copying of the raw png image
2260 cr = cairo_create(cs);
2261 cairo_scale(cr, squareSize/256., squareSize/256.);
2262 cairo_set_source_surface (cr, img, 0, 0);
2265 cairo_surface_destroy (img);
2273 for(p=0; pngPieceNames[p]; p++) {
2274 ScaleOnePiece(pngPieceNames[p], 0, p);
2275 ScaleOnePiece(pngPieceNames[p], 1, p);
2280 /* No built-in bitmaps */
2285 u_int ss = squareSize;
2287 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2290 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2291 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2292 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2293 pieceBitmapNames[piece],
2294 ss, kind == SOLID ? 's' : 'o');
2295 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2296 if(piece <= (int)WhiteKing)
2297 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2301 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2305 /* With built-in bitmaps */
2309 BuiltInBits* bib = builtInBits;
2312 u_int ss = squareSize;
2314 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2317 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2319 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2320 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2321 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2322 pieceBitmapNames[piece],
2323 ss, kind == SOLID ? 's' : 'o');
2324 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2325 bib->bits[kind][piece], ss, ss);
2326 if(piece <= (int)WhiteKing)
2327 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2331 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2337 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2342 char msg[MSG_SIZ], fullname[MSG_SIZ];
2344 if (*appData.bitmapDirectory != NULLCHAR) {
2345 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2346 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2347 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2348 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2349 &w, &h, pm, &x_hot, &y_hot);
2350 fprintf(stderr, "load %s\n", name);
2351 if (errcode != BitmapSuccess) {
2353 case BitmapOpenFailed:
2354 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2356 case BitmapFileInvalid:
2357 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2359 case BitmapNoMemory:
2360 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2364 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2368 fprintf(stderr, _("%s: %s...using built-in\n"),
2370 } else if (w != wreq || h != hreq) {
2372 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2373 programName, fullname, w, h, wreq, hreq);
2379 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2389 if (lineGap == 0) return;
2391 /* [HR] Split this into 2 loops for non-square boards. */
2393 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2394 gridSegments[i].x1 = 0;
2395 gridSegments[i].x2 =
2396 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2397 gridSegments[i].y1 = gridSegments[i].y2
2398 = lineGap / 2 + (i * (squareSize + lineGap));
2401 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2402 gridSegments[j + i].y1 = 0;
2403 gridSegments[j + i].y2 =
2404 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2405 gridSegments[j + i].x1 = gridSegments[j + i].x2
2406 = lineGap / 2 + (j * (squareSize + lineGap));
2411 MarkMenuItem (char *menuRef, int state)
2413 MenuItem *item = MenuNameToItem(menuRef);
2417 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2418 XtSetValues(item->handle, args, 1);
2423 EnableNamedMenuItem (char *menuRef, int state)
2425 MenuItem *item = MenuNameToItem(menuRef);
2427 if(item) XtSetSensitive(item->handle, state);
2431 EnableButtonBar (int state)
2433 XtSetSensitive(optList[W_BUTTON].handle, state);
2438 SetMenuEnables (Enables *enab)
2440 while (enab->name != NULL) {
2441 EnableNamedMenuItem(enab->name, enab->value);
2447 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2448 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2450 if(*nprms == 0) return;
2451 item = MenuNameToItem(prms[0]);
2452 if(item) ((MenuProc *) item->proc) ();
2456 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2458 RecentEngineEvent((int) (intptr_t) addr);
2462 AppendMenuItem (char *msg, int n)
2464 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2476 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2477 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2478 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2479 dmEnables[i].piece);
2480 XtSetSensitive(entry, p != NULL || !appData.testLegality
2481 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2482 && !appData.icsActive));
2484 while (p && *p++ == dmEnables[i].piece) count++;
2485 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2487 XtSetArg(args[j], XtNlabel, label); j++;
2488 XtSetValues(entry, args, j);
2494 do_flash_delay (unsigned long msec)
2500 DrawBorder (int x, int y, int type)
2505 cr = cairo_create(csBoardWindow);
2506 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
2507 cairo_rectangle(cr, x, y, squareSize+lineGap, squareSize+lineGap);
2508 SetPen(cr, lineGap, type == 1 ? appData.highlightSquareColor : appData.premoveHighlightColor, 0);
2513 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2515 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2516 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2518 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2519 if(textureW[kind] < W*squareSize)
2520 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2522 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2523 if(textureH[kind] < H*squareSize)
2524 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2526 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2531 DrawLogo (void *handle, void *logo)
2533 cairo_surface_t *img, *cs;
2537 if(!logo || !handle) return;
2538 cs = cairo_xlib_surface_create(xDisplay, XtWindow(handle), DefaultVisual(xDisplay, 0), appData.logoSize, appData.logoSize/2);
2539 img = cairo_image_surface_create_from_png (logo);
2540 w = cairo_image_surface_get_width (img);
2541 h = cairo_image_surface_get_height (img);
2542 cr = cairo_create(cs);
2543 cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
2544 cairo_set_source_surface (cr, img, 0, 0);
2547 cairo_surface_destroy (img);
2548 cairo_surface_destroy (cs);
2552 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2553 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2555 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2556 if(pngBoardBitmap[color]) {
2558 if(!fac && !cairoAnimate) return;
2560 cr = cairo_create (fac ? csBoardWindow : (cairo_surface_t *) dest);
2561 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2562 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2563 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2567 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2568 squareSize, squareSize, x*fac, y*fac);
2570 if (useImages && useImageSqs) {
2574 pm = xpmLightSquare;
2579 case 2: /* neutral */
2581 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2584 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2585 squareSize, squareSize, x*fac, y*fac);
2595 case 2: /* neutral */
2600 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2605 I split out the routines to draw a piece so that I could
2606 make a generic flash routine.
2609 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2611 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2612 switch (square_color) {
2614 case 2: /* neutral */
2616 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2617 ? *pieceToOutline(piece)
2618 : *pieceToSolid(piece),
2619 dest, bwPieceGC, 0, 0,
2620 squareSize, squareSize, x, y);
2623 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2624 ? *pieceToSolid(piece)
2625 : *pieceToOutline(piece),
2626 dest, wbPieceGC, 0, 0,
2627 squareSize, squareSize, x, y);
2633 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2635 switch (square_color) {
2637 case 2: /* neutral */
2639 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2640 ? *pieceToOutline(piece)
2641 : *pieceToSolid(piece),
2642 dest, bwPieceGC, 0, 0,
2643 squareSize, squareSize, x, y, 1);
2646 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2647 ? *pieceToSolid(piece)
2648 : *pieceToOutline(piece),
2649 dest, wbPieceGC, 0, 0,
2650 squareSize, squareSize, x, y, 1);
2656 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2658 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2659 switch (square_color) {
2661 XCopyPlane(xDisplay, *pieceToSolid(piece),
2662 dest, (int) piece < (int) BlackPawn
2663 ? wlPieceGC : blPieceGC, 0, 0,
2664 squareSize, squareSize, x, y, 1);
2667 XCopyPlane(xDisplay, *pieceToSolid(piece),
2668 dest, (int) piece < (int) BlackPawn
2669 ? wdPieceGC : bdPieceGC, 0, 0,
2670 squareSize, squareSize, x, y, 1);
2672 case 2: /* neutral */
2674 break; // should never contain pieces
2679 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2681 int kind, p = piece;
2683 switch (square_color) {
2685 case 2: /* neutral */
2687 if ((int)piece < (int) BlackPawn) {
2695 if ((int)piece < (int) BlackPawn) {
2703 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2704 if(useTexture & square_color+1) {
2705 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2706 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2707 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2708 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2709 XSetClipMask(xDisplay, wlPieceGC, None);
2710 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2712 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2713 dest, wlPieceGC, 0, 0,
2714 squareSize, squareSize, x, y);
2718 pngDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2720 int kind, p = piece;
2723 if ((int)piece < (int) BlackPawn) {
2729 if(appData.upsideDown && flipView) { p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2730 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2732 cr = cairo_create (csBoardWindow);
2733 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2738 typedef void (*DrawFunc)();
2743 if (appData.monoMode) {
2744 if (DefaultDepth(xDisplay, xScreen) == 1) {
2745 return monoDrawPiece_1bit;
2747 return monoDrawPiece;
2749 } else if(appData.pngDirectory[0]) {
2750 return pngDrawPiece;
2753 return colorDrawPieceImage;
2755 return colorDrawPiece;
2760 DrawDot (int marker, int x, int y, int r)
2764 cr = cairo_create(csBoardWindow);
2765 cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
2766 if(appData.monoMode) {
2767 SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
2768 cairo_stroke_preserve(cr);
2769 SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
2771 SetPen(cr, 2, marker == 2 ? "#FF0000" : "#FFFF00", 0);
2780 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2781 { // basic front-end board-draw function: takes care of everything that can be in square:
2782 // piece, background, coordinate/count, marker dot
2783 int direction, font_ascent, font_descent;
2784 XCharStruct overall;
2787 if (piece == EmptySquare) {
2788 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2790 drawfunc = ChooseDrawFunc();
2791 drawfunc(piece, square_color, x, y, xBoardWindow);
2794 if(align) { // square carries inscription (coord or piece count)
2796 GC hGC = align < 3 ? coordGC : countGC;
2797 // first calculate where it goes
2798 XTextExtents(countFontStruct, string, 1, &direction,
2799 &font_ascent, &font_descent, &overall);
2801 xx += squareSize - overall.width - 2;
2802 yy += squareSize - font_descent - 1;
2803 } else if (align == 2) {
2804 xx += 2, yy += font_ascent + 1;
2805 } else if (align == 3) {
2806 xx += squareSize - overall.width - 2;
2807 yy += font_ascent + 1;
2808 } else if (align == 4) {
2809 xx += 2, yy += font_ascent + 1;
2812 if (appData.monoMode) {
2813 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2815 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2819 if(marker) { // print fat marker dot, if requested
2820 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2825 FlashDelay (int flash_delay)
2827 XSync(xDisplay, False);
2828 if(flash_delay) do_flash_delay(flash_delay);
2832 Fraction (int x, int start, int stop)
2834 double f = ((double) x - start)/(stop - start);
2835 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2839 static WindowPlacement wpNew;
2842 CoDrag (Widget sh, WindowPlacement *wp)
2845 int j=0, touch=0, fudge = 2;
2846 GetActualPlacement(sh, wp);
2847 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2848 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2849 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2850 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2851 if(!touch ) return; // only windows that touch co-move
2852 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2853 int heightInc = wpNew.height - wpMain.height;
2854 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2855 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2856 wp->y += fracTop * heightInc;
2857 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2858 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2859 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2860 int widthInc = wpNew.width - wpMain.width;
2861 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2862 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2863 wp->y += fracLeft * widthInc;
2864 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2865 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2867 wp->x += wpNew.x - wpMain.x;
2868 wp->y += wpNew.y - wpMain.y;
2869 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2870 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2871 XtSetArg(args[j], XtNx, wp->x); j++;
2872 XtSetArg(args[j], XtNy, wp->y); j++;
2873 XtSetValues(sh, args, j);
2876 static XtIntervalId delayedDragID = 0;
2881 GetActualPlacement(shellWidget, &wpNew);
2882 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2883 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2884 return; // false alarm
2885 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2886 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2887 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2888 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2890 DrawPosition(True, NULL);
2891 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2898 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2900 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2904 EventProc (Widget widget, caddr_t unused, XEvent *event)
2906 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2907 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2910 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
2913 Color (char *col, int n)
2916 sscanf(col, "#%x", &c);
2922 SetPen (cairo_t *cr, float w, char *col, int dash)
2924 static const double dotted[] = {4.0, 4.0};
2925 static int len = sizeof(dotted) / sizeof(dotted[0]);
2926 cairo_set_line_width (cr, w);
2927 cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
2928 if(dash) cairo_set_dash (cr, dotted, len, 0.0);
2931 void DrawSeekAxis( int x, int y, int xTo, int yTo )
2936 cr = cairo_create (csBoardWindow);
2938 cairo_move_to (cr, x, y);
2939 cairo_line_to(cr, xTo, yTo );
2941 SetPen(cr, 2, "#000000", 0);
2948 void DrawSeekBackground( int left, int top, int right, int bottom )
2950 cairo_t *cr = cairo_create (csBoardWindow);
2952 cairo_rectangle (cr, left, top, right-left, bottom-top);
2954 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
2961 void DrawSeekText(char *buf, int x, int y)
2963 cairo_t *cr = cairo_create (csBoardWindow);
2965 cairo_select_font_face (cr, "Sans",
2966 CAIRO_FONT_SLANT_NORMAL,
2967 CAIRO_FONT_WEIGHT_NORMAL);
2969 cairo_set_font_size (cr, 12.0);
2971 cairo_move_to (cr, x, y+4);
2972 cairo_show_text( cr, buf);
2974 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
2981 void DrawSeekDot(int x, int y, int colorNr)
2983 cairo_t *cr = cairo_create (csBoardWindow);
2984 int square = colorNr & 0x80;
2988 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2990 cairo_arc(cr, x, y, squareSize/8, 0.0, 2*M_PI);
2992 SetPen(cr, 2, "#000000", 0);
2993 cairo_stroke_preserve(cr);
2995 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
2996 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
2997 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
3008 int boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3009 int boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3010 if(!csBoardWindow) csBoardWindow = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), boardWidth, boardHeight);
3021 /* draws a grid starting around Nx, Ny squares starting at x,y */
3027 cr = cairo_create (csBoardWindow);
3029 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
3030 SetPen(cr, lineGap, "#000000", 0);
3033 for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
3035 cairo_move_to (cr, gridSegments[i].x1, gridSegments[i].y1);
3036 cairo_line_to (cr, gridSegments[i].x2, gridSegments[i].y2);
3047 * event handler for redrawing the board
3050 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3052 DrawPosition(True, NULL);
3057 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3058 { // [HGM] pv: walk PV
3059 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3062 static int savedIndex; /* gross that this is global */
3065 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3068 XawTextPosition index, dummy;
3071 XawTextGetSelectionPos(w, &index, &dummy);
3072 XtSetArg(arg, XtNstring, &val);
3073 XtGetValues(w, &arg, 1);
3074 ReplaceComment(savedIndex, val);
3075 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3076 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3080 EditCommentPopUp (int index, char *title, char *text)
3083 if (text == NULL) text = "";
3084 NewCommentPopup(title, text, index);
3088 CommentPopUp (char *title, char *text)
3090 savedIndex = currentMove; // [HGM] vari
3091 NewCommentPopup(title, text, currentMove);
3097 PopDown(CommentDlg);
3101 /* Disable all user input other than deleting the window */
3102 static int frozen = 0;
3108 /* Grab by a widget that doesn't accept input */
3109 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
3113 /* Undo a FreezeUI */
3117 if (!frozen) return;
3118 XtRemoveGrab(optList[W_MESSG].handle);
3126 static int oldPausing = FALSE;
3127 static GameMode oldmode = (GameMode) -1;
3130 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3132 if (pausing != oldPausing) {
3133 oldPausing = pausing;
3134 MarkMenuItem("Mode.Pause", pausing);
3136 if (appData.showButtonBar) {
3137 /* Always toggle, don't set. Previous code messes up when
3138 invoked while the button is pressed, as releasing it
3139 toggles the state again. */
3142 XtSetArg(args[0], XtNbackground, &oldbg);
3143 XtSetArg(args[1], XtNforeground, &oldfg);
3144 XtGetValues(optList[W_PAUSE].handle,
3146 XtSetArg(args[0], XtNbackground, oldfg);
3147 XtSetArg(args[1], XtNforeground, oldbg);
3149 XtSetValues(optList[W_PAUSE].handle, args, 2);
3153 wname = ModeToWidgetName(oldmode);
3154 if (wname != NULL) {
3155 MarkMenuItem(wname, False);
3157 wname = ModeToWidgetName(gameMode);
3158 if (wname != NULL) {
3159 MarkMenuItem(wname, True);
3162 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
3164 /* Maybe all the enables should be handled here, not just this one */
3165 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
3167 DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
3172 * Button/menu procedures
3175 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3176 char *selected_fen_position=NULL;
3179 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3180 Atom *type_return, XtPointer *value_return,
3181 unsigned long *length_return, int *format_return)
3183 char *selection_tmp;
3185 // if (!selected_fen_position) return False; /* should never happen */
3186 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3187 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3188 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3191 if (f == NULL) return False;
3195 selection_tmp = XtMalloc(len + 1);
3196 count = fread(selection_tmp, 1, len, f);
3199 XtFree(selection_tmp);
3202 selection_tmp[len] = NULLCHAR;
3204 /* note: since no XtSelectionDoneProc was registered, Xt will
3205 * automatically call XtFree on the value returned. So have to
3206 * make a copy of it allocated with XtMalloc */
3207 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3208 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3211 *value_return=selection_tmp;
3212 *length_return=strlen(selection_tmp);
3213 *type_return=*target;
3214 *format_return = 8; /* bits per byte */
3216 } else if (*target == XA_TARGETS(xDisplay)) {
3217 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3218 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3219 targets_tmp[1] = XA_STRING;
3220 *value_return = targets_tmp;
3221 *type_return = XA_ATOM;
3224 // This code leads to a read of value_return out of bounds on 64-bit systems.
3225 // Other code which I have seen always sets *format_return to 32 independent of
3226 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3227 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3228 *format_return = 8 * sizeof(Atom);
3229 if (*format_return > 32) {
3230 *length_return *= *format_return / 32;
3231 *format_return = 32;
3234 *format_return = 32;
3242 /* note: when called from menu all parameters are NULL, so no clue what the
3243 * Widget which was clicked on was, or what the click event was
3246 CopySomething (char *src)
3248 selected_fen_position = src;
3250 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3251 * have a notion of a position that is selected but not copied.
3252 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3254 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3256 SendPositionSelection,
3257 NULL/* lose_ownership_proc */ ,
3258 NULL/* transfer_done_proc */);
3259 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3261 SendPositionSelection,
3262 NULL/* lose_ownership_proc */ ,
3263 NULL/* transfer_done_proc */);
3266 /* function called when the data to Paste is ready */
3268 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3269 Atom *type, XtPointer value, unsigned long *len, int *format)
3272 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3273 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3274 EditPositionPasteFEN(fenstr);
3278 /* called when Paste Position button is pressed,
3279 * all parameters will be NULL */
3281 PastePositionProc ()
3283 XtGetSelectionValue(menuBarWidget,
3284 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3285 /* (XtSelectionCallbackProc) */ PastePositionCB,
3286 NULL, /* client_data passed to PastePositionCB */
3288 /* better to use the time field from the event that triggered the
3289 * call to this function, but that isn't trivial to get
3296 /* note: when called from menu all parameters are NULL, so no clue what the
3297 * Widget which was clicked on was, or what the click event was
3299 /* function called when the data to Paste is ready */
3301 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3302 Atom *type, XtPointer value, unsigned long *len, int *format)
3305 if (value == NULL || *len == 0) {
3306 return; /* nothing had been selected to copy */
3308 f = fopen(gamePasteFilename, "w");
3310 DisplayError(_("Can't open temp file"), errno);
3313 fwrite(value, 1, *len, f);
3316 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3319 /* called when Paste Game button is pressed,
3320 * all parameters will be NULL */
3324 XtGetSelectionValue(menuBarWidget,
3325 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3326 /* (XtSelectionCallbackProc) */ PasteGameCB,
3327 NULL, /* client_data passed to PasteGameCB */
3329 /* better to use the time field from the event that triggered the
3330 * call to this function, but that isn't trivial to get
3339 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3346 { // bassic primitive for determining if modifier keys are pressed
3347 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3350 XQueryKeymap(xDisplay,keys);
3351 for(i=0; i<6; i++) {
3353 j = XKeysymToKeycode(xDisplay, codes[i]);
3354 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3360 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3364 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3365 if ( n == 1 && *buf >= 32 // printable
3366 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3367 ) BoxAutoPopUp (buf);
3371 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3372 { // [HGM] input: let up-arrow recall previous line from history
3377 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3378 { // [HGM] input: let down-arrow recall next line from history
3383 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3389 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3391 if (!TempBackwardActive) {
3392 TempBackwardActive = True;
3398 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3400 /* Check to see if triggered by a key release event for a repeating key.
3401 * If so the next queued event will be a key press of the same key at the same time */
3402 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3404 XPeekEvent(xDisplay, &next);
3405 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3406 next.xkey.keycode == event->xkey.keycode)
3410 TempBackwardActive = False;
3414 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3415 { // called as key binding
3418 if (nprms && *nprms > 0)
3422 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3428 { // called from menu
3429 ManInner(NULL, NULL, NULL, NULL);
3433 SetWindowTitle (char *text, char *title, char *icon)
3437 if (appData.titleInWindow) {
3439 XtSetArg(args[i], XtNlabel, text); i++;
3440 XtSetValues(titleWidget, args, i);
3443 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3444 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3445 XtSetValues(shellWidget, args, i);
3446 XSync(xDisplay, False);
3451 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3457 DisplayIcsInteractionTitle (String message)
3459 if (oldICSInteractionTitle == NULL) {
3460 /* Magic to find the old window title, adapted from vim */
3461 char *wina = getenv("WINDOWID");
3463 Window win = (Window) atoi(wina);
3464 Window root, parent, *children;
3465 unsigned int nchildren;
3466 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3468 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3469 if (!XQueryTree(xDisplay, win, &root, &parent,
3470 &children, &nchildren)) break;
3471 if (children) XFree((void *)children);
3472 if (parent == root || parent == 0) break;
3475 XSetErrorHandler(oldHandler);
3477 if (oldICSInteractionTitle == NULL) {
3478 oldICSInteractionTitle = "xterm";
3481 printf("\033]0;%s\007", message);
3486 XtIntervalId delayedEventTimerXID = 0;
3487 DelayedEventCallback delayedEventCallback = 0;
3492 delayedEventTimerXID = 0;
3493 delayedEventCallback();
3497 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3499 if(delayedEventTimerXID && delayedEventCallback == cb)
3500 // [HGM] alive: replace, rather than add or flush identical event
3501 XtRemoveTimeOut(delayedEventTimerXID);
3502 delayedEventCallback = cb;
3503 delayedEventTimerXID =
3504 XtAppAddTimeOut(appContext, millisec,
3505 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3508 DelayedEventCallback
3511 if (delayedEventTimerXID) {
3512 return delayedEventCallback;
3519 CancelDelayedEvent ()
3521 if (delayedEventTimerXID) {
3522 XtRemoveTimeOut(delayedEventTimerXID);
3523 delayedEventTimerXID = 0;
3527 XtIntervalId loadGameTimerXID = 0;
3530 LoadGameTimerRunning ()
3532 return loadGameTimerXID != 0;
3536 StopLoadGameTimer ()
3538 if (loadGameTimerXID != 0) {
3539 XtRemoveTimeOut(loadGameTimerXID);
3540 loadGameTimerXID = 0;
3548 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3550 loadGameTimerXID = 0;
3555 StartLoadGameTimer (long millisec)
3558 XtAppAddTimeOut(appContext, millisec,
3559 (XtTimerCallbackProc) LoadGameTimerCallback,
3563 XtIntervalId analysisClockXID = 0;
3566 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3568 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3569 || appData.icsEngineAnalyze) { // [DM]
3570 AnalysisPeriodicEvent(0);
3571 StartAnalysisClock();
3576 StartAnalysisClock ()
3579 XtAppAddTimeOut(appContext, 2000,
3580 (XtTimerCallbackProc) AnalysisClockCallback,
3584 XtIntervalId clockTimerXID = 0;
3587 ClockTimerRunning ()
3589 return clockTimerXID != 0;
3595 if (clockTimerXID != 0) {
3596 XtRemoveTimeOut(clockTimerXID);
3605 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3612 StartClockTimer (long millisec)
3615 XtAppAddTimeOut(appContext, millisec,
3616 (XtTimerCallbackProc) ClockTimerCallback,
3621 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3625 Widget w = (Widget) opt->handle;
3627 /* check for low time warning */
3628 Pixel foregroundOrWarningColor = timerForegroundPixel;
3631 appData.lowTimeWarning &&
3632 (timer / 1000) < appData.icsAlarmTime)
3633 foregroundOrWarningColor = lowTimeWarningColor;
3635 if (appData.clockMode) {
3636 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
3637 XtSetArg(args[0], XtNlabel, buf);
3639 snprintf(buf, MSG_SIZ, "%s ", color);
3640 XtSetArg(args[0], XtNlabel, buf);
3645 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3646 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3648 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3649 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3652 XtSetValues(w, args, 3);
3655 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3658 SetClockIcon (int color)
3661 Pixmap pm = *clockIcons[color];
3662 if (iconPixmap != pm) {
3664 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3665 XtSetValues(shellWidget, args, 1);
3670 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3672 InputSource *is = (InputSource *) closure;
3677 if (is->lineByLine) {
3678 count = read(is->fd, is->unused,
3679 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3681 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3684 is->unused += count;
3686 while (p < is->unused) {
3687 q = memchr(p, '\n', is->unused - p);
3688 if (q == NULL) break;
3690 (is->func)(is, is->closure, p, q - p, 0);
3694 while (p < is->unused) {
3699 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3704 (is->func)(is, is->closure, is->buf, count, error);
3709 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3712 ChildProc *cp = (ChildProc *) pr;
3714 is = (InputSource *) calloc(1, sizeof(InputSource));
3715 is->lineByLine = lineByLine;
3719 is->fd = fileno(stdin);
3721 is->kind = cp->kind;
3722 is->fd = cp->fdFrom;
3725 is->unused = is->buf;
3728 is->xid = XtAppAddInput(appContext, is->fd,
3729 (XtPointer) (XtInputReadMask),
3730 (XtInputCallbackProc) DoInputCallback,
3732 is->closure = closure;
3733 return (InputSourceRef) is;
3737 RemoveInputSource (InputSourceRef isr)
3739 InputSource *is = (InputSource *) isr;
3741 if (is->xid == 0) return;
3742 XtRemoveInput(is->xid);
3746 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3748 /* Masks for XPM pieces. Black and white pieces can have
3749 different shapes, but in the interest of retaining my
3750 sanity pieces must have the same outline on both light
3751 and dark squares, and all pieces must use the same
3752 background square colors/images. */
3754 static int xpmDone = 0;
3755 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3756 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3757 static cairo_surface_t *c_animBufs[3*NrOfAnims]; // newBuf, saveBuf
3760 CreateAnimMasks (int pieceDepth)
3766 unsigned long plane;
3769 /* Need a bitmap just to get a GC with right depth */
3770 buf = XCreatePixmap(xDisplay, xBoardWindow,
3772 values.foreground = 1;
3773 values.background = 0;
3774 /* Don't use XtGetGC, not read only */
3775 maskGC = XCreateGC(xDisplay, buf,
3776 GCForeground | GCBackground, &values);
3777 XFreePixmap(xDisplay, buf);
3779 buf = XCreatePixmap(xDisplay, xBoardWindow,
3780 squareSize, squareSize, pieceDepth);
3781 values.foreground = XBlackPixel(xDisplay, xScreen);
3782 values.background = XWhitePixel(xDisplay, xScreen);
3783 bufGC = XCreateGC(xDisplay, buf,
3784 GCForeground | GCBackground, &values);
3786 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3787 /* Begin with empty mask */
3788 if(!xpmDone) // [HGM] pieces: keep using existing
3789 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3790 squareSize, squareSize, 1);
3791 XSetFunction(xDisplay, maskGC, GXclear);
3792 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3793 0, 0, squareSize, squareSize);
3795 /* Take a copy of the piece */
3800 XSetFunction(xDisplay, bufGC, GXcopy);
3801 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3803 0, 0, squareSize, squareSize, 0, 0);
3805 /* XOR the background (light) over the piece */
3806 XSetFunction(xDisplay, bufGC, GXxor);
3808 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3809 0, 0, squareSize, squareSize, 0, 0);
3811 XSetForeground(xDisplay, bufGC, lightSquareColor);
3812 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3815 /* We now have an inverted piece image with the background
3816 erased. Construct mask by just selecting all the non-zero
3817 pixels - no need to reconstruct the original image. */
3818 XSetFunction(xDisplay, maskGC, GXor);
3820 /* Might be quicker to download an XImage and create bitmap
3821 data from it rather than this N copies per piece, but it
3822 only takes a fraction of a second and there is a much
3823 longer delay for loading the pieces. */
3824 for (n = 0; n < pieceDepth; n ++) {
3825 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3826 0, 0, squareSize, squareSize,
3832 XFreePixmap(xDisplay, buf);
3833 XFreeGC(xDisplay, bufGC);
3834 XFreeGC(xDisplay, maskGC);
3838 InitAnimState (AnimNr anr, XWindowAttributes *info)
3844 DrawSeekOpen(); // set cs to board widget
3845 c_animBufs[anr+4] = csBoardWindow;
3846 c_animBufs[anr+2] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3847 c_animBufs[anr] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3850 /* Each buffer is square size, same depth as window */
3851 animBufs[anr+4] = xBoardWindow;
3852 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3853 squareSize, squareSize, info->depth);
3854 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3855 squareSize, squareSize, info->depth);
3857 /* Create a plain GC for blitting */
3858 mask = GCForeground | GCBackground | GCFunction |
3859 GCPlaneMask | GCGraphicsExposures;
3860 values.foreground = XBlackPixel(xDisplay, xScreen);
3861 values.background = XWhitePixel(xDisplay, xScreen);
3862 values.function = GXcopy;
3863 values.plane_mask = AllPlanes;
3864 values.graphics_exposures = False;
3865 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3867 /* Piece will be copied from an existing context at
3868 the start of each new animation/drag. */
3869 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3871 /* Outline will be a read-only copy of an existing */
3872 animGCs[anr+4] = None;
3878 XWindowAttributes info;
3880 if (xpmDone && gameInfo.variant == oldVariant) return;
3881 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3882 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3884 InitAnimState(Game, &info);
3885 InitAnimState(Player, &info);
3887 /* For XPM pieces, we need bitmaps to use as masks. */
3889 CreateAnimMasks(info.depth), xpmDone = 1;
3894 static Boolean frameWaiting;
3897 FrameAlarm (int sig)
3899 frameWaiting = False;
3900 /* In case System-V style signals. Needed?? */
3901 signal(SIGALRM, FrameAlarm);
3905 FrameDelay (int time)
3907 struct itimerval delay;
3909 XSync(xDisplay, False);
3912 frameWaiting = True;
3913 signal(SIGALRM, FrameAlarm);
3914 delay.it_interval.tv_sec =
3915 delay.it_value.tv_sec = time / 1000;
3916 delay.it_interval.tv_usec =
3917 delay.it_value.tv_usec = (time % 1000) * 1000;
3918 setitimer(ITIMER_REAL, &delay, NULL);
3919 while (frameWaiting) pause();
3920 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3921 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3922 setitimer(ITIMER_REAL, &delay, NULL);
3929 FrameDelay (int time)
3931 XSync(xDisplay, False);
3933 usleep(time * 1000);
3939 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3943 /* Bitmap for piece being moved. */
3944 if (appData.monoMode) {
3945 *mask = *pieceToSolid(piece);
3946 } else if (useImages) {
3948 *mask = xpmMask[piece];
3950 *mask = ximMaskPm[piece];
3953 *mask = *pieceToSolid(piece);
3956 /* GC for piece being moved. Square color doesn't matter, but
3957 since it gets modified we make a copy of the original. */
3959 if (appData.monoMode)
3964 if (appData.monoMode)
3969 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3971 /* Outline only used in mono mode and is not modified */
3973 *outline = bwPieceGC;
3975 *outline = wbPieceGC;
3979 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
3984 /* Draw solid rectangle which will be clipped to shape of piece */
3985 XFillRectangle(xDisplay, dest, clip,
3986 0, 0, squareSize, squareSize);
3987 if (appData.monoMode)
3988 /* Also draw outline in contrasting color for black
3989 on black / white on white cases */
3990 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3991 0, 0, squareSize, squareSize, 0, 0, 1);
3993 /* Copy the piece */
3998 if(appData.upsideDown && flipView) kind ^= 2;
3999 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4001 0, 0, squareSize, squareSize,
4007 CairoOverlayPiece (ChessSquare piece, cairo_surface_t *dest)
4009 static ChessSquare oldPiece = -1;
4010 static cairo_t *pieceSource;
4011 if(piece != oldPiece) { // try make it faster by only changing cr if we need other piece
4012 if(pieceSource) cairo_destroy (pieceSource);
4013 pieceSource = cairo_create (dest);
4014 cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
4017 cairo_paint(pieceSource);
4021 InsertPiece (AnimNr anr, ChessSquare piece)
4024 CairoOverlayPiece(piece, c_animBufs[anr]);
4026 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
4030 DrawBlank (AnimNr anr, int x, int y, int startColor)
4033 BlankSquare(x, y, startColor, EmptySquare, (Drawable) c_animBufs[anr+2], 0);
4035 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
4038 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
4039 int srcX, int srcY, int width, int height, int destX, int destY)
4042 cairo_t *cr = cairo_create (c_animBufs[anr+destBuf]);
4043 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
4044 cairo_rectangle (cr, destX, destY, width, height);
4048 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
4049 srcX, srcY, width, height, destX, destY);
4053 SetDragPiece (AnimNr anr, ChessSquare piece)
4056 if(cairoAnimate) return;
4057 /* The piece will be drawn using its own bitmap as a matte */
4058 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
4059 XSetClipMask(xDisplay, animGCs[anr+2], mask);
4062 /* [AS] Arrow highlighting support */
4064 void DrawPolygon(Pnt arrow[], int nr)
4065 { // for now on own surface; eventually this should become a global that is only destroyed on resize
4066 cairo_surface_t *boardSurface;
4069 int w = lineGap + BOARD_WIDTH * (squareSize + lineGap);
4070 int h = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
4071 boardSurface = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), w, h);
4072 cr = cairo_create (boardSurface);
4073 cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
4074 for (i=0;i<nr;i++) {
4075 cairo_line_to(cr, arrow[i].x, arrow[i].y);
4077 if(appData.monoMode) { // should we always outline arrow?
4078 cairo_line_to(cr, arrow[0].x, arrow[0].y);
4079 SetPen(cr, 2, "#000000", 0);
4080 cairo_stroke_preserve(cr);
4082 SetPen(cr, 2, appData.highlightSquareColor, 0);
4087 cairo_surface_destroy (boardSurface);
4091 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
4093 char buf[MSG_SIZ], *logoName = buf;
4094 if(appData.logo[n][0]) {
4095 logoName = appData.logo[n];
4096 } else if(appData.autoLogo) {
4097 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
4098 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
4099 } else if(appData.directory[n] && appData.directory[n][0]) {
4100 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
4104 { ASSIGN(cps->programLogo, logoName); }
4108 UpdateLogos (int displ)
4110 if(optList[W_WHITE-1].handle == NULL) return;
4111 LoadLogo(&first, 0, 0);
4112 LoadLogo(&second, 1, appData.icsActive);
4113 if(displ) DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);