2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
64 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.h>
68 # if HAVE_SYS_SOCKET_H
69 # include <sys/socket.h>
70 # include <netinet/in.h>
72 # else /* not HAVE_SYS_SOCKET_H */
73 # if HAVE_LAN_SOCKET_H
74 # include <lan/socket.h>
76 # include <lan/netdb.h>
77 # else /* not HAVE_LAN_SOCKET_H */
78 # define OMIT_SOCKETS 1
79 # endif /* not HAVE_LAN_SOCKET_H */
80 # endif /* not HAVE_SYS_SOCKET_H */
81 #endif /* !OMIT_SOCKETS */
86 #else /* not STDC_HEADERS */
87 extern char *getenv();
90 # else /* not HAVE_STRING_H */
92 # endif /* not HAVE_STRING_H */
93 #endif /* not STDC_HEADERS */
96 # include <sys/fcntl.h>
97 #else /* not HAVE_SYS_FCNTL_H */
100 # endif /* HAVE_FCNTL_H */
101 #endif /* not HAVE_SYS_FCNTL_H */
103 #if HAVE_SYS_SYSTEMINFO_H
104 # include <sys/systeminfo.h>
105 #endif /* HAVE_SYS_SYSTEMINFO_H */
107 #if TIME_WITH_SYS_TIME
108 # include <sys/time.h>
112 # include <sys/time.h>
123 # include <sys/wait.h>
128 # define NAMLEN(dirent) strlen((dirent)->d_name)
129 # define HAVE_DIR_STRUCT
131 # define dirent direct
132 # define NAMLEN(dirent) (dirent)->d_namlen
134 # include <sys/ndir.h>
135 # define HAVE_DIR_STRUCT
138 # include <sys/dir.h>
139 # define HAVE_DIR_STRUCT
143 # define HAVE_DIR_STRUCT
151 #include <X11/Intrinsic.h>
152 #include <X11/StringDefs.h>
153 #include <X11/Shell.h>
154 #include <X11/cursorfont.h>
155 #include <X11/Xatom.h>
156 #include <X11/Xmu/Atoms.h>
158 #include <X11/Xaw3d/Dialog.h>
159 #include <X11/Xaw3d/Form.h>
160 #include <X11/Xaw3d/List.h>
161 #include <X11/Xaw3d/Label.h>
162 #include <X11/Xaw3d/SimpleMenu.h>
163 #include <X11/Xaw3d/SmeBSB.h>
164 #include <X11/Xaw3d/SmeLine.h>
165 #include <X11/Xaw3d/Box.h>
166 #include <X11/Xaw3d/MenuButton.h>
167 #include <X11/Xaw3d/Text.h>
168 #include <X11/Xaw3d/AsciiText.h>
170 #include <X11/Xaw/Dialog.h>
171 #include <X11/Xaw/Form.h>
172 #include <X11/Xaw/List.h>
173 #include <X11/Xaw/Label.h>
174 #include <X11/Xaw/SimpleMenu.h>
175 #include <X11/Xaw/SmeBSB.h>
176 #include <X11/Xaw/SmeLine.h>
177 #include <X11/Xaw/Box.h>
178 #include <X11/Xaw/MenuButton.h>
179 #include <X11/Xaw/Text.h>
180 #include <X11/Xaw/AsciiText.h>
183 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
188 #include "pixmaps/pixmaps.h"
189 #define IMAGE_EXT "xpm"
191 #define IMAGE_EXT "xim"
192 #include "bitmaps/bitmaps.h"
195 #include "bitmaps/icon_white.bm"
196 #include "bitmaps/icon_black.bm"
197 #include "bitmaps/checkmark.bm"
199 #include "frontend.h"
201 #include "backendz.h"
205 #include "xgamelist.h"
206 #include "xhistory.h"
207 #include "xevalgraph.h"
208 #include "xedittags.h"
212 #include "engineoutput.h"
221 #define usleep(t) _sleep2(((t)+500)/1000)
225 # define _(s) gettext (s)
226 # define N_(s) gettext_noop (s)
232 int main P((int argc, char **argv));
233 RETSIGTYPE CmailSigHandler P((int sig));
234 RETSIGTYPE IntSigHandler P((int sig));
235 RETSIGTYPE TermSizeSigHandler P((int sig));
236 static void CreateGCs P((int redo));
237 static void CreateAnyPieces P((void));
238 void CreateXIMPieces P((void));
239 void CreateXPMPieces P((void));
240 void CreatePNGPieces P((void));
241 void CreateXPMBoard P((char *s, int n));
242 void CreatePieces P((void));
243 Widget CreateMenuBar P((Menu *mb, int boardWidth));
245 char *InsertPxlSize P((char *pattern, int targetPxlSize));
246 XFontSet CreateFontSet P((char *base_fnt_lst));
248 char *FindFont P((char *pattern, int targetPxlSize));
250 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
251 u_int wreq, u_int hreq));
252 void CreateGrid P((void));
253 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
254 void DelayedDrag P((void));
255 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
256 void HandlePV P((Widget w, XEvent * event,
257 String * params, Cardinal * nParams));
258 void DrawPositionProc P((Widget w, XEvent *event,
259 String *prms, Cardinal *nprms));
260 void CommentClick P((Widget w, XEvent * event,
261 String * params, Cardinal * nParams));
262 void ICSInputBoxPopUp P((void));
263 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
264 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
265 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
266 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
267 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
268 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
269 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
270 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
271 Boolean TempBackwardActive = False;
272 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
273 void DisplayMove P((int moveNumber));
274 void ICSInitScript P((void));
275 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
276 void update_ics_width P(());
277 int CopyMemoProc P(());
280 * XBoard depends on Xt R4 or higher
282 int xtVersion = XtSpecificationRelease;
287 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
288 highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
289 Pixel lowTimeWarningColor;
290 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
291 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
293 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
294 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
295 Option *optList; // contains all widgets of main window
296 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
298 XFontSet fontSet, clockFontSet;
301 XFontStruct *clockFontStruct;
303 Font coordFontID, countFontID;
304 XFontStruct *coordFontStruct, *countFontStruct;
305 XtAppContext appContext;
308 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
310 Position commentX = -1, commentY = -1;
311 Dimension commentW, commentH;
312 typedef unsigned int BoardSize;
314 Boolean chessProgram;
316 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
317 int smallLayout = 0, tinyLayout = 0,
318 marginW, marginH, // [HGM] for run-time resizing
319 fromX = -1, fromY = -1, toX, toY, commentUp = False,
320 errorExitStatus = -1, defaultLineGap;
321 Dimension textHeight;
322 Pixel timerForegroundPixel, timerBackgroundPixel;
323 Pixel buttonForegroundPixel, buttonBackgroundPixel;
324 char *chessDir, *programName, *programVersion;
325 Boolean alwaysOnTop = False;
326 char *icsTextMenuString;
328 char *firstChessProgramNames;
329 char *secondChessProgramNames;
331 WindowPlacement wpMain;
332 WindowPlacement wpConsole;
333 WindowPlacement wpComment;
334 WindowPlacement wpMoveHistory;
335 WindowPlacement wpEvalGraph;
336 WindowPlacement wpEngineOutput;
337 WindowPlacement wpGameList;
338 WindowPlacement wpTags;
343 Boolean cairoAnimate;
344 static cairo_surface_t *csBoardWindow, *csBoardBackup;
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 //--------------------------------------------------------------------------------------------
933 // delete surfaces after size becomes invalid, so they will be recreated
934 if(csBoardWindow) cairo_surface_destroy(csBoardWindow);
935 if(csBoardBackup) cairo_surface_destroy(csBoardBackup);
936 csBoardWindow = csBoardBackup = NULL;
939 #define BoardSize int
941 InitDrawingSizes (BoardSize boardSize, int flags)
942 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
943 Dimension boardWidth, boardHeight, w, h;
945 static Dimension oldWidth, oldHeight;
946 static VariantClass oldVariant;
947 static int oldMono = -1, oldTwoBoards = 0;
949 if(!formWidget) return;
951 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
952 oldTwoBoards = twoBoards;
954 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
955 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
956 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
958 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
960 oldWidth = boardWidth; oldHeight = boardHeight;
965 * Inhibit shell resizing.
967 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
968 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
969 shellArgs[4].value = shellArgs[2].value = w;
970 shellArgs[5].value = shellArgs[3].value = h;
971 XtSetValues(shellWidget, &shellArgs[0], cairoAnimate ? 2 : 6);
973 XSync(xDisplay, False);
977 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
980 if(gameInfo.variant != oldVariant) { // and only if variant changed
985 for(p=0; p<=(int)WhiteKing; p++)
986 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
987 if(gameInfo.variant == VariantShogi) {
988 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
989 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
990 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
991 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
992 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
995 if(gameInfo.variant == VariantGothic) {
996 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
999 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1000 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1001 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1004 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1005 for(p=0; p<=(int)WhiteKing; p++)
1006 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1007 if(gameInfo.variant == VariantShogi) {
1008 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1009 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1010 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1011 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1012 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1015 if(gameInfo.variant == VariantGothic) {
1016 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1019 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1020 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1021 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1026 for(i=0; i<2; i++) {
1028 for(p=0; p<=(int)WhiteKing; p++)
1029 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1030 if(gameInfo.variant == VariantShogi) {
1031 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1032 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1033 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1034 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1035 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1038 if(gameInfo.variant == VariantGothic) {
1039 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1042 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1043 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1044 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1048 for(i=0; i<2; i++) {
1050 printf("Copy pieces\n");
1051 for(p=0; p<=(int)WhiteKing; p++)
1052 pngPieceBitmaps[i][p] = pngPieceBitmaps2[i][p]; // defaults
1054 oldMono = -10; // kludge to force recreation of animation masks
1055 oldVariant = gameInfo.variant;
1058 if(appData.monoMode != oldMono || cairoAnimate)
1061 oldMono = appData.monoMode;
1065 MakeOneColor (char *name, Pixel *color)
1067 XrmValue vFrom, vTo;
1068 if (!appData.monoMode) {
1069 vFrom.addr = (caddr_t) name;
1070 vFrom.size = strlen(name);
1071 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1072 if (vTo.addr == NULL) {
1073 appData.monoMode = True;
1076 *color = *(Pixel *) vTo.addr;
1084 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1085 int forceMono = False;
1087 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1088 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1089 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1090 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1091 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1092 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1093 if (appData.lowTimeWarning)
1094 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1095 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1096 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1103 { // [HGM] taken out of main
1105 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1106 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1107 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1109 if (appData.bitmapDirectory[0] != NULLCHAR) {
1113 CreateXPMBoard(appData.liteBackTextureFile, 1);
1114 CreateXPMBoard(appData.darkBackTextureFile, 0);
1116 if (appData.pngDirectory[0] != NULLCHAR) { // for now do in parallel
1121 /* Create regular pieces */
1122 if (!useImages) CreatePieces();
1127 InitDrawingParams ()
1129 MakeColors(); CreateGCs(True);
1134 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1135 { // detervtomine what fonts to use, and create them
1139 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1140 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1141 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1142 appData.font = fontTable[MESSAGE_FONT][squareSize];
1143 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1144 appData.coordFont = fontTable[COORD_FONT][squareSize];
1147 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1148 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1149 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1150 fontSet = CreateFontSet(appData.font);
1151 clockFontSet = CreateFontSet(appData.clockFont);
1153 /* For the coordFont, use the 0th font of the fontset. */
1154 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1155 XFontStruct **font_struct_list;
1156 XFontSetExtents *fontSize;
1157 char **font_name_list;
1158 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1159 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1160 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1161 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1162 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1165 appData.font = FindFont(appData.font, fontPxlSize);
1166 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1167 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1168 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1169 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1170 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1171 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1172 // textHeight in !NLS mode!
1174 countFontID = coordFontID; // [HGM] holdings
1175 countFontStruct = coordFontStruct;
1177 xdb = XtDatabase(xDisplay);
1179 XrmPutLineResource(&xdb, "*international: True");
1180 vTo.size = sizeof(XFontSet);
1181 vTo.addr = (XtPointer) &fontSet;
1182 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1184 XrmPutStringResource(&xdb, "*font", appData.font);
1189 PrintArg (ArgType t)
1194 case ArgInt: p = " N"; break;
1195 case ArgString: p = " STR"; break;
1196 case ArgBoolean: p = " TF"; break;
1197 case ArgSettingsFilename:
1198 case ArgFilename: p = " FILE"; break;
1199 case ArgX: p = " Nx"; break;
1200 case ArgY: p = " Ny"; break;
1201 case ArgAttribs: p = " TEXTCOL"; break;
1202 case ArgColor: p = " COL"; break;
1203 case ArgFont: p = " FONT"; break;
1204 case ArgBoardSize: p = " SIZE"; break;
1205 case ArgFloat: p = " FLOAT"; break;
1210 case ArgCommSettings:
1221 ArgDescriptor *q, *p = argDescriptors+5;
1222 printf("\nXBoard accepts the following options:\n"
1223 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1224 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1225 " SIZE = board-size spec(s)\n"
1226 " Within parentheses are short forms, or options to set to true or false.\n"
1227 " Persistent options (saved in the settings file) are marked with *)\n\n");
1229 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1230 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1231 if(p->save) strcat(buf+len, "*");
1232 for(q=p+1; q->argLoc == p->argLoc; q++) {
1233 if(q->argName[0] == '-') continue;
1234 strcat(buf+len, q == p+1 ? " (" : " ");
1235 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1237 if(q != p+1) strcat(buf+len, ")");
1239 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1242 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1246 main (int argc, char **argv)
1248 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1249 XSetWindowAttributes window_attributes;
1251 Dimension boardWidth, boardHeight, w, h;
1253 int forceMono = False;
1255 srandom(time(0)); // [HGM] book: make random truly random
1257 setbuf(stdout, NULL);
1258 setbuf(stderr, NULL);
1261 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1262 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1266 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1271 programName = strrchr(argv[0], '/');
1272 if (programName == NULL)
1273 programName = argv[0];
1278 XtSetLanguageProc(NULL, NULL, NULL);
1279 if (appData.debugMode) {
1280 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1283 bindtextdomain(PACKAGE, LOCALEDIR);
1284 textdomain(PACKAGE);
1287 appData.boardSize = "";
1288 InitAppData(ConvertToLine(argc, argv));
1290 if (p == NULL) p = "/tmp";
1291 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1292 gameCopyFilename = (char*) malloc(i);
1293 gamePasteFilename = (char*) malloc(i);
1294 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1295 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1297 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1298 static char buf[MSG_SIZ];
1299 EscapeExpand(buf, appData.firstInitString);
1300 appData.firstInitString = strdup(buf);
1301 EscapeExpand(buf, appData.secondInitString);
1302 appData.secondInitString = strdup(buf);
1303 EscapeExpand(buf, appData.firstComputerString);
1304 appData.firstComputerString = strdup(buf);
1305 EscapeExpand(buf, appData.secondComputerString);
1306 appData.secondComputerString = strdup(buf);
1309 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1312 if (chdir(chessDir) != 0) {
1313 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1319 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1320 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1321 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1322 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1325 setbuf(debugFP, NULL);
1328 /* [HGM,HR] make sure board size is acceptable */
1329 if(appData.NrFiles > BOARD_FILES ||
1330 appData.NrRanks > BOARD_RANKS )
1331 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1334 /* This feature does not work; animation needs a rewrite */
1335 appData.highlightDragging = FALSE;
1339 gameInfo.variant = StringToVariant(appData.variant);
1340 InitPosition(FALSE);
1343 XtAppInitialize(&appContext, "XBoard", shellOptions,
1344 XtNumber(shellOptions),
1345 &argc, argv, xboardResources, NULL, 0);
1347 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1348 clientResources, XtNumber(clientResources),
1351 xDisplay = XtDisplay(shellWidget);
1352 xScreen = DefaultScreen(xDisplay);
1353 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1356 * determine size, based on supplied or remembered -size, or screen size
1358 if (isdigit(appData.boardSize[0])) {
1359 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1360 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1361 &fontPxlSize, &smallLayout, &tinyLayout);
1363 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1364 programName, appData.boardSize);
1368 /* Find some defaults; use the nearest known size */
1369 SizeDefaults *szd, *nearest;
1370 int distance = 99999;
1371 nearest = szd = sizeDefaults;
1372 while (szd->name != NULL) {
1373 if (abs(szd->squareSize - squareSize) < distance) {
1375 distance = abs(szd->squareSize - squareSize);
1376 if (distance == 0) break;
1380 if (i < 2) lineGap = nearest->lineGap;
1381 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1382 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1383 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1384 if (i < 6) smallLayout = nearest->smallLayout;
1385 if (i < 7) tinyLayout = nearest->tinyLayout;
1388 SizeDefaults *szd = sizeDefaults;
1389 if (*appData.boardSize == NULLCHAR) {
1390 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1391 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1394 if (szd->name == NULL) szd--;
1395 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1397 while (szd->name != NULL &&
1398 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1399 if (szd->name == NULL) {
1400 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1401 programName, appData.boardSize);
1405 squareSize = szd->squareSize;
1406 lineGap = szd->lineGap;
1407 clockFontPxlSize = szd->clockFontPxlSize;
1408 coordFontPxlSize = szd->coordFontPxlSize;
1409 fontPxlSize = szd->fontPxlSize;
1410 smallLayout = szd->smallLayout;
1411 tinyLayout = szd->tinyLayout;
1412 // [HGM] font: use defaults from settings file if available and not overruled
1415 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1416 if (strlen(appData.pixmapDirectory) > 0) {
1417 p = ExpandPathName(appData.pixmapDirectory);
1419 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1420 appData.pixmapDirectory);
1423 if (appData.debugMode) {
1424 fprintf(stderr, _("\
1425 XBoard square size (hint): %d\n\
1426 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1428 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1429 if (appData.debugMode) {
1430 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1433 defaultLineGap = lineGap;
1434 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1436 /* [HR] height treated separately (hacked) */
1437 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1438 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1441 * Determine what fonts to use.
1443 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1446 * Detect if there are not enough colors available and adapt.
1448 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1449 appData.monoMode = True;
1452 forceMono = MakeColors();
1455 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1457 appData.monoMode = True;
1460 if (appData.monoMode && appData.debugMode) {
1461 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1462 (unsigned long) XWhitePixel(xDisplay, xScreen),
1463 (unsigned long) XBlackPixel(xDisplay, xScreen));
1466 ParseIcsTextColors();
1468 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1474 layoutName = "tinyLayout";
1475 } else if (smallLayout) {
1476 layoutName = "smallLayout";
1478 layoutName = "normalLayout";
1481 optList = BoardPopUp(squareSize, lineGap, (void*)
1487 boardWidget = optList[W_BOARD].handle;
1488 menuBarWidget = optList[W_MENU].handle;
1489 dropMenu = optList[W_DROP].handle;
1490 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1491 formWidget = XtParent(boardWidget);
1492 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1493 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1494 XtGetValues(optList[W_WHITE].handle, args, 2);
1495 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1496 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1497 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1498 XtGetValues(optList[W_PAUSE].handle, args, 2);
1500 AppendEnginesToMenu(appData.recentEngineList);
1502 xBoardWindow = XtWindow(boardWidget);
1504 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1505 // not need to go into InitDrawingSizes().
1508 * Create X checkmark bitmap and initialize option menu checks.
1510 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1511 checkmark_bits, checkmark_width, checkmark_height);
1517 ReadBitmap(&wIconPixmap, "icon_white.bm",
1518 icon_white_bits, icon_white_width, icon_white_height);
1519 ReadBitmap(&bIconPixmap, "icon_black.bm",
1520 icon_black_bits, icon_black_width, icon_black_height);
1521 iconPixmap = wIconPixmap;
1523 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1524 XtSetValues(shellWidget, args, i);
1527 * Create a cursor for the board widget.
1529 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1530 XChangeWindowAttributes(xDisplay, xBoardWindow,
1531 CWCursor, &window_attributes);
1534 * Inhibit shell resizing.
1538 cairoAnimate = *appData.pngDirectory && useTexture == 3
1539 && strstr(appData.liteBackTextureFile, ".png") && strstr(appData.darkBackTextureFile, ".png");
1541 shellArgs[0].value = (XtArgVal) &w;
1542 shellArgs[1].value = (XtArgVal) &h;
1543 XtGetValues(shellWidget, shellArgs, 2);
1544 shellArgs[4].value = shellArgs[2].value = w;
1545 shellArgs[5].value = shellArgs[3].value = h;
1546 if(!cairoAnimate) XtSetValues(shellWidget, &shellArgs[2], 4);
1547 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1548 marginH = h - boardHeight;
1550 CatchDeleteWindow(shellWidget, "QuitProc");
1555 if(appData.logoSize)
1556 { // locate and read user logo
1558 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1559 ASSIGN(userLogo, buf);
1562 if (appData.animate || appData.animateDragging)
1565 XtAugmentTranslations(formWidget,
1566 XtParseTranslationTable(globalTranslations));
1568 XtAddEventHandler(formWidget, KeyPressMask, False,
1569 (XtEventHandler) MoveTypeInProc, NULL);
1570 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1571 (XtEventHandler) EventProc, NULL);
1573 /* [AS] Restore layout */
1574 if( wpMoveHistory.visible ) {
1578 if( wpEvalGraph.visible )
1583 if( wpEngineOutput.visible ) {
1584 EngineOutputPopUp();
1589 if (errorExitStatus == -1) {
1590 if (appData.icsActive) {
1591 /* We now wait until we see "login:" from the ICS before
1592 sending the logon script (problems with timestamp otherwise) */
1593 /*ICSInitScript();*/
1594 if (appData.icsInputBox) ICSInputBoxPopUp();
1598 signal(SIGWINCH, TermSizeSigHandler);
1600 signal(SIGINT, IntSigHandler);
1601 signal(SIGTERM, IntSigHandler);
1602 if (*appData.cmailGameName != NULLCHAR) {
1603 signal(SIGUSR1, CmailSigHandler);
1607 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1610 // XtSetKeyboardFocus(shellWidget, formWidget);
1611 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1613 XtAppMainLoop(appContext);
1614 if (appData.debugMode) fclose(debugFP); // [DM] debug
1619 TermSizeSigHandler (int sig)
1625 IntSigHandler (int sig)
1631 CmailSigHandler (int sig)
1636 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1638 /* Activate call-back function CmailSigHandlerCallBack() */
1639 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1641 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1645 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1648 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1650 /**** end signal code ****/
1653 #define Abs(n) ((n)<0 ? -(n) : (n))
1657 InsertPxlSize (char *pattern, int targetPxlSize)
1659 char *base_fnt_lst, strInt[12], *p, *q;
1660 int alternatives, i, len, strIntLen;
1663 * Replace the "*" (if present) in the pixel-size slot of each
1664 * alternative with the targetPxlSize.
1668 while ((p = strchr(p, ',')) != NULL) {
1672 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1673 strIntLen = strlen(strInt);
1674 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1678 while (alternatives--) {
1679 char *comma = strchr(p, ',');
1680 for (i=0; i<14; i++) {
1681 char *hyphen = strchr(p, '-');
1683 if (comma && hyphen > comma) break;
1684 len = hyphen + 1 - p;
1685 if (i == 7 && *p == '*' && len == 2) {
1687 memcpy(q, strInt, strIntLen);
1697 len = comma + 1 - p;
1704 return base_fnt_lst;
1708 CreateFontSet (char *base_fnt_lst)
1711 char **missing_list;
1715 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1716 &missing_list, &missing_count, &def_string);
1717 if (appData.debugMode) {
1719 XFontStruct **font_struct_list;
1720 char **font_name_list;
1721 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1723 fprintf(debugFP, " got list %s, locale %s\n",
1724 XBaseFontNameListOfFontSet(fntSet),
1725 XLocaleOfFontSet(fntSet));
1726 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1727 for (i = 0; i < count; i++) {
1728 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1731 for (i = 0; i < missing_count; i++) {
1732 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1735 if (fntSet == NULL) {
1736 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1741 #else // not ENABLE_NLS
1743 * Find a font that matches "pattern" that is as close as
1744 * possible to the targetPxlSize. Prefer fonts that are k
1745 * pixels smaller to fonts that are k pixels larger. The
1746 * pattern must be in the X Consortium standard format,
1747 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1748 * The return value should be freed with XtFree when no
1752 FindFont (char *pattern, int targetPxlSize)
1754 char **fonts, *p, *best, *scalable, *scalableTail;
1755 int i, j, nfonts, minerr, err, pxlSize;
1757 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1759 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1760 programName, pattern);
1767 for (i=0; i<nfonts; i++) {
1770 if (*p != '-') continue;
1772 if (*p == NULLCHAR) break;
1773 if (*p++ == '-') j++;
1775 if (j < 7) continue;
1778 scalable = fonts[i];
1781 err = pxlSize - targetPxlSize;
1782 if (Abs(err) < Abs(minerr) ||
1783 (minerr > 0 && err < 0 && -err == minerr)) {
1789 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1790 /* If the error is too big and there is a scalable font,
1791 use the scalable font. */
1792 int headlen = scalableTail - scalable;
1793 p = (char *) XtMalloc(strlen(scalable) + 10);
1794 while (isdigit(*scalableTail)) scalableTail++;
1795 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1797 p = (char *) XtMalloc(strlen(best) + 2);
1798 safeStrCpy(p, best, strlen(best)+1 );
1800 if (appData.debugMode) {
1801 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1802 pattern, targetPxlSize, p);
1804 XFreeFontNames(fonts);
1811 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1812 // must be called before all non-first callse to CreateGCs()
1813 XtReleaseGC(shellWidget, highlineGC);
1814 XtReleaseGC(shellWidget, lightSquareGC);
1815 XtReleaseGC(shellWidget, darkSquareGC);
1816 XtReleaseGC(shellWidget, lineGC);
1817 if (appData.monoMode) {
1818 if (DefaultDepth(xDisplay, xScreen) == 1) {
1819 XtReleaseGC(shellWidget, wbPieceGC);
1821 XtReleaseGC(shellWidget, bwPieceGC);
1824 XtReleaseGC(shellWidget, prelineGC);
1825 XtReleaseGC(shellWidget, wdPieceGC);
1826 XtReleaseGC(shellWidget, wlPieceGC);
1827 XtReleaseGC(shellWidget, bdPieceGC);
1828 XtReleaseGC(shellWidget, blPieceGC);
1833 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1835 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1836 | GCBackground | GCFunction | GCPlaneMask;
1837 gc_values->foreground = foreground;
1838 gc_values->background = background;
1839 return XtGetGC(shellWidget, value_mask, gc_values);
1843 CreateGCs (int redo)
1845 XGCValues gc_values;
1847 Pixel white = XWhitePixel(xDisplay, xScreen);
1848 Pixel black = XBlackPixel(xDisplay, xScreen);
1850 gc_values.plane_mask = AllPlanes;
1851 gc_values.line_width = lineGap;
1852 gc_values.line_style = LineSolid;
1853 gc_values.function = GXcopy;
1856 DeleteGCs(); // called a second time; clean up old GCs first
1857 } else { // [HGM] grid and font GCs created on first call only
1858 coordGC = CreateOneGC(&gc_values, black, white);
1859 XSetFont(xDisplay, coordGC, coordFontID);
1861 // [HGM] make font for holdings counts (white on black)
1862 countGC = CreateOneGC(&gc_values, white, black);
1863 XSetFont(xDisplay, countGC, countFontID);
1865 lineGC = CreateOneGC(&gc_values, black, black);
1867 if (appData.monoMode) {
1869 highlineGC = CreateOneGC(&gc_values, white, white);
1870 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1871 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1873 if (DefaultDepth(xDisplay, xScreen) == 1) {
1874 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1875 gc_values.function = GXcopyInverted;
1876 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1877 gc_values.function = GXcopy;
1878 if (XBlackPixel(xDisplay, xScreen) == 1) {
1879 bwPieceGC = darkSquareGC;
1880 wbPieceGC = copyInvertedGC;
1882 bwPieceGC = copyInvertedGC;
1883 wbPieceGC = lightSquareGC;
1888 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1889 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1890 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1891 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1892 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1893 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1894 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1895 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1900 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1908 fp = fopen(filename, "rb");
1910 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1917 for (y=0; y<h; ++y) {
1918 for (x=0; x<h; ++x) {
1923 XPutPixel(xim, x, y, blackPieceColor);
1925 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1928 XPutPixel(xim, x, y, darkSquareColor);
1930 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1933 XPutPixel(xim, x, y, whitePieceColor);
1935 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1938 XPutPixel(xim, x, y, lightSquareColor);
1940 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1948 /* create Pixmap of piece */
1949 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1951 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1954 /* create Pixmap of clipmask
1955 Note: We assume the white/black pieces have the same
1956 outline, so we make only 6 masks. This is okay
1957 since the XPM clipmask routines do the same. */
1959 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1961 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1964 /* now create the 1-bit version */
1965 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1968 values.foreground = 1;
1969 values.background = 0;
1971 /* Don't use XtGetGC, not read only */
1972 maskGC = XCreateGC(xDisplay, *mask,
1973 GCForeground | GCBackground, &values);
1974 XCopyPlane(xDisplay, temp, *mask, maskGC,
1975 0, 0, squareSize, squareSize, 0, 0, 1);
1976 XFreePixmap(xDisplay, temp);
1981 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1989 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1994 /* The XSynchronize calls were copied from CreatePieces.
1995 Not sure if needed, but can't hurt */
1996 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1999 /* temp needed by loadXIM() */
2000 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2001 0, 0, ss, ss, AllPlanes, XYPixmap);
2003 if (strlen(appData.pixmapDirectory) == 0) {
2007 if (appData.monoMode) {
2008 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2012 fprintf(stderr, _("\nLoading XIMs...\n"));
2014 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2015 fprintf(stderr, "%d", piece+1);
2016 for (kind=0; kind<4; kind++) {
2017 fprintf(stderr, ".");
2018 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2019 ExpandPathName(appData.pixmapDirectory),
2020 piece <= (int) WhiteKing ? "" : "w",
2021 pieceBitmapNames[piece],
2023 ximPieceBitmap[kind][piece] =
2024 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2025 0, 0, ss, ss, AllPlanes, XYPixmap);
2026 if (appData.debugMode)
2027 fprintf(stderr, _("(File:%s:) "), buf);
2028 loadXIM(ximPieceBitmap[kind][piece],
2030 &(xpmPieceBitmap2[kind][piece]),
2031 &(ximMaskPm2[piece]));
2032 if(piece <= (int)WhiteKing)
2033 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2035 fprintf(stderr," ");
2037 /* Load light and dark squares */
2038 /* If the LSQ and DSQ pieces don't exist, we will
2039 draw them with solid squares. */
2040 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2041 if (access(buf, 0) != 0) {
2045 fprintf(stderr, _("light square "));
2047 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2048 0, 0, ss, ss, AllPlanes, XYPixmap);
2049 if (appData.debugMode)
2050 fprintf(stderr, _("(File:%s:) "), buf);
2052 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2053 fprintf(stderr, _("dark square "));
2054 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2055 ExpandPathName(appData.pixmapDirectory), ss);
2056 if (appData.debugMode)
2057 fprintf(stderr, _("(File:%s:) "), buf);
2059 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2060 0, 0, ss, ss, AllPlanes, XYPixmap);
2061 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2062 xpmJailSquare = xpmLightSquare;
2064 fprintf(stderr, _("Done.\n"));
2066 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2069 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2073 CreateXPMBoard (char *s, int kind)
2077 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2078 if(strstr(s, ".png")) {
2079 cairo_surface_t *img = cairo_image_surface_create_from_png (s);
2081 useTexture |= kind + 1; pngBoardBitmap[kind] = img;
2082 textureW[kind] = cairo_image_surface_get_width (img);
2083 textureH[kind] = cairo_image_surface_get_height (img);
2086 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2087 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2093 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2094 // thisroutine has to be called t free the old piece pixmaps
2096 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2097 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2099 XFreePixmap(xDisplay, xpmLightSquare);
2100 XFreePixmap(xDisplay, xpmDarkSquare);
2109 u_int ss = squareSize;
2111 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2112 XpmColorSymbol symbols[4];
2113 static int redo = False;
2115 if(redo) FreeXPMPieces(); else redo = 1;
2117 /* The XSynchronize calls were copied from CreatePieces.
2118 Not sure if needed, but can't hurt */
2119 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2121 /* Setup translations so piece colors match square colors */
2122 symbols[0].name = "light_piece";
2123 symbols[0].value = appData.whitePieceColor;
2124 symbols[1].name = "dark_piece";
2125 symbols[1].value = appData.blackPieceColor;
2126 symbols[2].name = "light_square";
2127 symbols[2].value = appData.lightSquareColor;
2128 symbols[3].name = "dark_square";
2129 symbols[3].value = appData.darkSquareColor;
2131 attr.valuemask = XpmColorSymbols;
2132 attr.colorsymbols = symbols;
2133 attr.numsymbols = 4;
2135 if (appData.monoMode) {
2136 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2140 if (strlen(appData.pixmapDirectory) == 0) {
2141 XpmPieces* pieces = builtInXpms;
2144 while (pieces->size != squareSize && pieces->size) pieces++;
2145 if (!pieces->size) {
2146 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2149 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2150 for (kind=0; kind<4; kind++) {
2152 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2153 pieces->xpm[piece][kind],
2154 &(xpmPieceBitmap2[kind][piece]),
2155 NULL, &attr)) != 0) {
2156 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2160 if(piece <= (int) WhiteKing)
2161 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2165 xpmJailSquare = xpmLightSquare;
2169 fprintf(stderr, _("\nLoading XPMs...\n"));
2172 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2173 fprintf(stderr, "%d ", piece+1);
2174 for (kind=0; kind<4; kind++) {
2175 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2176 ExpandPathName(appData.pixmapDirectory),
2177 piece > (int) WhiteKing ? "w" : "",
2178 pieceBitmapNames[piece],
2180 if (appData.debugMode) {
2181 fprintf(stderr, _("(File:%s:) "), buf);
2183 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2184 &(xpmPieceBitmap2[kind][piece]),
2185 NULL, &attr)) != 0) {
2186 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2187 // [HGM] missing: read of unorthodox piece failed; substitute King.
2188 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2189 ExpandPathName(appData.pixmapDirectory),
2191 if (appData.debugMode) {
2192 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2194 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2195 &(xpmPieceBitmap2[kind][piece]),
2199 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2204 if(piece <= (int) WhiteKing)
2205 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2208 /* Load light and dark squares */
2209 /* If the LSQ and DSQ pieces don't exist, we will
2210 draw them with solid squares. */
2211 fprintf(stderr, _("light square "));
2212 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2213 if (access(buf, 0) != 0) {
2217 if (appData.debugMode)
2218 fprintf(stderr, _("(File:%s:) "), buf);
2220 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2221 &xpmLightSquare, NULL, &attr)) != 0) {
2222 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2225 fprintf(stderr, _("dark square "));
2226 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2227 ExpandPathName(appData.pixmapDirectory), ss);
2228 if (appData.debugMode) {
2229 fprintf(stderr, _("(File:%s:) "), buf);
2231 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2232 &xpmDarkSquare, NULL, &attr)) != 0) {
2233 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2237 xpmJailSquare = xpmLightSquare;
2238 fprintf(stderr, _("Done.\n"));
2240 oldVariant = -1; // kludge to force re-makig of animation masks
2241 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2244 #endif /* HAVE_LIBXPM */
2246 char *pngPieceNames[] = // must be in same order as internal piece encoding
2247 { "Pawn", "Knight", "Bishop", "Rook", "Queen", "Advisor", "Elephant", "Archbishop", "Marshall", "Gold", "Commoner",
2248 "Canon", "Nightrider", "CrownedBishop", "CrownedRook", "Princess", "Chancellor", "Hawk", "Lance", "Cobra", "Unicorn", "King",
2249 "GoldKnight", "GoldLance", "GoldPawn", "GoldSilver", NULL
2253 ScaleOnePiece (char *name, int color, int piece)
2257 cairo_surface_t *img, *cs;
2259 static cairo_surface_t *pngPieceImages[2][(int)BlackPawn+4]; // png 256 x 256 images
2261 if((img = pngPieceImages[color][piece]) == NULL) { // if PNG file for this piece was not yet read, read it now and store it
2262 snprintf(buf, MSG_SIZ, "%s/%s%s.png", appData.pngDirectory, color ? "Black" : "White", pngPieceNames[piece]);
2263 pngPieceImages[color][piece] = img = cairo_image_surface_create_from_png (buf);
2264 w = cairo_image_surface_get_width (img);
2265 h = cairo_image_surface_get_height (img);
2266 if(w != 64 || h != 64) { printf("Bad png size %dx%d in %s\n", w, h, buf); exit(1); }
2268 // create new bitmap to hold scaled piece image (and remove any old)
2269 if(pngPieceBitmaps2[color][piece]) cairo_surface_destroy (pngPieceBitmaps2[color][piece]);
2270 pngPieceBitmaps2[color][piece] = cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
2271 if(piece <= WhiteKing) pngPieceBitmaps[color][piece] = cs;
2272 // scaled copying of the raw png image
2273 cr = cairo_create(cs);
2274 cairo_scale(cr, squareSize/64., squareSize/64.);
2275 cairo_set_source_surface (cr, img, 0, 0);
2285 for(p=0; pngPieceNames[p]; p++) {
2286 ScaleOnePiece(pngPieceNames[p], 0, p);
2287 ScaleOnePiece(pngPieceNames[p], 1, p);
2292 /* No built-in bitmaps */
2297 u_int ss = squareSize;
2299 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2302 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2303 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2304 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2305 pieceBitmapNames[piece],
2306 ss, kind == SOLID ? 's' : 'o');
2307 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2308 if(piece <= (int)WhiteKing)
2309 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2313 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2317 /* With built-in bitmaps */
2321 BuiltInBits* bib = builtInBits;
2324 u_int ss = squareSize;
2326 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2329 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2331 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2332 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2333 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2334 pieceBitmapNames[piece],
2335 ss, kind == SOLID ? 's' : 'o');
2336 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2337 bib->bits[kind][piece], ss, ss);
2338 if(piece <= (int)WhiteKing)
2339 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2343 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2349 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2354 char msg[MSG_SIZ], fullname[MSG_SIZ];
2356 if (*appData.bitmapDirectory != NULLCHAR) {
2357 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2358 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2359 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2360 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2361 &w, &h, pm, &x_hot, &y_hot);
2362 fprintf(stderr, "load %s\n", name);
2363 if (errcode != BitmapSuccess) {
2365 case BitmapOpenFailed:
2366 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2368 case BitmapFileInvalid:
2369 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2371 case BitmapNoMemory:
2372 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2376 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2380 fprintf(stderr, _("%s: %s...using built-in\n"),
2382 } else if (w != wreq || h != hreq) {
2384 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2385 programName, fullname, w, h, wreq, hreq);
2391 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2401 if (lineGap == 0) return;
2403 /* [HR] Split this into 2 loops for non-square boards. */
2405 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2406 gridSegments[i].x1 = 0;
2407 gridSegments[i].x2 =
2408 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2409 gridSegments[i].y1 = gridSegments[i].y2
2410 = lineGap / 2 + (i * (squareSize + lineGap));
2413 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2414 gridSegments[j + i].y1 = 0;
2415 gridSegments[j + i].y2 =
2416 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2417 gridSegments[j + i].x1 = gridSegments[j + i].x2
2418 = lineGap / 2 + (j * (squareSize + lineGap));
2423 MarkMenuItem (char *menuRef, int state)
2425 MenuItem *item = MenuNameToItem(menuRef);
2429 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2430 XtSetValues(item->handle, args, 1);
2435 EnableNamedMenuItem (char *menuRef, int state)
2437 MenuItem *item = MenuNameToItem(menuRef);
2439 if(item) XtSetSensitive(item->handle, state);
2443 EnableButtonBar (int state)
2445 XtSetSensitive(optList[W_BUTTON].handle, state);
2450 SetMenuEnables (Enables *enab)
2452 while (enab->name != NULL) {
2453 EnableNamedMenuItem(enab->name, enab->value);
2459 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2460 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2462 if(*nprms == 0) return;
2463 item = MenuNameToItem(prms[0]);
2464 if(item) ((MenuProc *) item->proc) ();
2468 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2470 RecentEngineEvent((int) (intptr_t) addr);
2474 AppendMenuItem (char *msg, int n)
2476 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2488 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2489 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2490 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2491 dmEnables[i].piece);
2492 XtSetSensitive(entry, p != NULL || !appData.testLegality
2493 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2494 && !appData.icsActive));
2496 while (p && *p++ == dmEnables[i].piece) count++;
2497 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2499 XtSetArg(args[j], XtNlabel, label); j++;
2500 XtSetValues(entry, args, j);
2506 do_flash_delay (unsigned long msec)
2512 DoDrawBorder (cairo_surface_t *cs, int x, int y, int type)
2517 cr = cairo_create(cs);
2518 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
2519 cairo_rectangle(cr, x, y, squareSize+lineGap, squareSize+lineGap);
2520 SetPen(cr, lineGap, type == 1 ? appData.highlightSquareColor : appData.premoveHighlightColor, 0);
2525 DrawBorder (int x, int y, int type)
2527 DoDrawBorder(csBoardWindow, x, y, type);
2528 DoDrawBorder(csBoardBackup, x, y, type);
2532 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2534 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2535 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2537 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2538 if(textureW[kind] < W*squareSize)
2539 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2541 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2542 if(textureH[kind] < H*squareSize)
2543 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2545 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2550 DrawLogo (void *handle, void *logo)
2552 cairo_surface_t *img, *cs;
2556 if(!logo || !handle) return;
2557 cs = cairo_xlib_surface_create(xDisplay, XtWindow(handle), DefaultVisual(xDisplay, 0), appData.logoSize, appData.logoSize/2);
2558 img = cairo_image_surface_create_from_png (logo);
2559 w = cairo_image_surface_get_width (img);
2560 h = cairo_image_surface_get_height (img);
2561 cr = cairo_create(cs);
2562 cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
2563 cairo_set_source_surface (cr, img, 0, 0);
2566 cairo_surface_destroy (img);
2567 cairo_surface_destroy (cs);
2571 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2572 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2574 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2575 if(pngBoardBitmap[color]) {
2577 if(!fac && !cairoAnimate) return;
2579 cr = cairo_create (fac ? csBoardWindow : (cairo_surface_t *) dest);
2580 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2581 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2582 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2586 cr = cairo_create (csBoardBackup);
2587 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2588 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2589 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2594 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2595 squareSize, squareSize, x*fac, y*fac);
2597 if (useImages && useImageSqs) {
2601 pm = xpmLightSquare;
2606 case 2: /* neutral */
2608 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2611 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2612 squareSize, squareSize, x*fac, y*fac);
2622 case 2: /* neutral */
2627 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2632 I split out the routines to draw a piece so that I could
2633 make a generic flash routine.
2636 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2638 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2639 switch (square_color) {
2641 case 2: /* neutral */
2643 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2644 ? *pieceToOutline(piece)
2645 : *pieceToSolid(piece),
2646 dest, bwPieceGC, 0, 0,
2647 squareSize, squareSize, x, y);
2650 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2651 ? *pieceToSolid(piece)
2652 : *pieceToOutline(piece),
2653 dest, wbPieceGC, 0, 0,
2654 squareSize, squareSize, x, y);
2660 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2662 switch (square_color) {
2664 case 2: /* neutral */
2666 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2667 ? *pieceToOutline(piece)
2668 : *pieceToSolid(piece),
2669 dest, bwPieceGC, 0, 0,
2670 squareSize, squareSize, x, y, 1);
2673 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2674 ? *pieceToSolid(piece)
2675 : *pieceToOutline(piece),
2676 dest, wbPieceGC, 0, 0,
2677 squareSize, squareSize, x, y, 1);
2683 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2685 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2686 switch (square_color) {
2688 XCopyPlane(xDisplay, *pieceToSolid(piece),
2689 dest, (int) piece < (int) BlackPawn
2690 ? wlPieceGC : blPieceGC, 0, 0,
2691 squareSize, squareSize, x, y, 1);
2694 XCopyPlane(xDisplay, *pieceToSolid(piece),
2695 dest, (int) piece < (int) BlackPawn
2696 ? wdPieceGC : bdPieceGC, 0, 0,
2697 squareSize, squareSize, x, y, 1);
2699 case 2: /* neutral */
2701 break; // should never contain pieces
2706 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2708 int kind, p = piece;
2710 switch (square_color) {
2712 case 2: /* neutral */
2714 if ((int)piece < (int) BlackPawn) {
2722 if ((int)piece < (int) BlackPawn) {
2730 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2731 if(useTexture & square_color+1) {
2732 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2733 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2734 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2735 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2736 XSetClipMask(xDisplay, wlPieceGC, None);
2737 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2739 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2740 dest, wlPieceGC, 0, 0,
2741 squareSize, squareSize, x, y);
2745 pngDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2747 int kind, p = piece;
2750 if ((int)piece < (int) BlackPawn) {
2756 if(appData.upsideDown && flipView) { p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2757 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2759 cr = cairo_create (csBoardWindow);
2760 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2763 cr = cairo_create (csBoardBackup);
2764 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2769 typedef void (*DrawFunc)();
2774 if (appData.monoMode) {
2775 if (DefaultDepth(xDisplay, xScreen) == 1) {
2776 return monoDrawPiece_1bit;
2778 return monoDrawPiece;
2780 } else if(appData.pngDirectory[0]) {
2781 return pngDrawPiece;
2784 return colorDrawPieceImage;
2786 return colorDrawPiece;
2791 DoDrawDot (int marker, int x, int y, int r, cairo_surface_t *cs)
2795 cr = cairo_create(cs);
2796 cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
2797 if(appData.monoMode) {
2798 SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
2799 cairo_stroke_preserve(cr);
2800 SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
2802 SetPen(cr, 2, marker == 2 ? "#FF0000" : "#FFFF00", 0);
2811 DrawDot (int marker, int x, int y, int r)
2813 DoDrawDot(marker, x, y, r, csBoardWindow);
2814 DoDrawDot(marker, x, y, r, csBoardBackup);
2818 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2819 { // basic front-end board-draw function: takes care of everything that can be in square:
2820 // piece, background, coordinate/count, marker dot
2821 int direction, font_ascent, font_descent;
2822 XCharStruct overall;
2825 if (piece == EmptySquare) {
2826 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2828 drawfunc = ChooseDrawFunc();
2829 drawfunc(piece, square_color, x, y, xBoardWindow);
2832 if(align) { // square carries inscription (coord or piece count)
2834 GC hGC = align < 3 ? coordGC : countGC;
2835 // first calculate where it goes
2836 XTextExtents(countFontStruct, string, 1, &direction,
2837 &font_ascent, &font_descent, &overall);
2839 xx += squareSize - overall.width - 2;
2840 yy += squareSize - font_descent - 1;
2841 } else if (align == 2) {
2842 xx += 2, yy += font_ascent + 1;
2843 } else if (align == 3) {
2844 xx += squareSize - overall.width - 2;
2845 yy += font_ascent + 1;
2846 } else if (align == 4) {
2847 xx += 2, yy += font_ascent + 1;
2850 if (appData.monoMode) {
2851 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2853 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2857 if(marker) { // print fat marker dot, if requested
2858 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2863 FlashDelay (int flash_delay)
2865 XSync(xDisplay, False);
2866 if(flash_delay) do_flash_delay(flash_delay);
2870 Fraction (int x, int start, int stop)
2872 double f = ((double) x - start)/(stop - start);
2873 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2877 static WindowPlacement wpNew;
2880 CoDrag (Widget sh, WindowPlacement *wp)
2883 int j=0, touch=0, fudge = 2;
2884 GetActualPlacement(sh, wp);
2885 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2886 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2887 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2888 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2889 if(!touch ) return; // only windows that touch co-move
2890 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2891 int heightInc = wpNew.height - wpMain.height;
2892 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2893 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2894 wp->y += fracTop * heightInc;
2895 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2896 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2897 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2898 int widthInc = wpNew.width - wpMain.width;
2899 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2900 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2901 wp->y += fracLeft * widthInc;
2902 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2903 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2905 wp->x += wpNew.x - wpMain.x;
2906 wp->y += wpNew.y - wpMain.y;
2907 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2908 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2909 XtSetArg(args[j], XtNx, wp->x); j++;
2910 XtSetArg(args[j], XtNy, wp->y); j++;
2911 XtSetValues(sh, args, j);
2915 ReSize (WindowPlacement *wp)
2918 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
2919 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
2920 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
2921 if(sqy < sqx) sqx = sqy;
2922 if(sqx != squareSize) {
2923 squareSize = sqx; // adopt new square size
2925 CreatePNGPieces(); // make newly scaled pieces
2926 InitDrawingSizes(0, 0); // creates grid etc.
2930 static XtIntervalId delayedDragID = 0;
2939 GetActualPlacement(shellWidget, &wpNew);
2940 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2941 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
2942 busy = 0; return; // false alarm
2945 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2946 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2947 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2948 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2950 DrawPosition(True, NULL);
2951 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2959 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2961 XtAppAddTimeOut(appContext, 100, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2965 EventProc (Widget widget, caddr_t unused, XEvent *event)
2967 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2968 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2971 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
2974 Color (char *col, int n)
2977 sscanf(col, "#%x", &c);
2983 SetPen (cairo_t *cr, float w, char *col, int dash)
2985 static const double dotted[] = {4.0, 4.0};
2986 static int len = sizeof(dotted) / sizeof(dotted[0]);
2987 cairo_set_line_width (cr, w);
2988 cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
2989 if(dash) cairo_set_dash (cr, dotted, len, 0.0);
2992 void DrawSeekAxis( int x, int y, int xTo, int yTo )
2997 cr = cairo_create (csBoardWindow);
2999 cairo_move_to (cr, x, y);
3000 cairo_line_to(cr, xTo, yTo );
3002 SetPen(cr, 2, "#000000", 0);
3009 void DrawSeekBackground( int left, int top, int right, int bottom )
3011 cairo_t *cr = cairo_create (csBoardWindow);
3013 cairo_rectangle (cr, left, top, right-left, bottom-top);
3015 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
3022 void DrawSeekText(char *buf, int x, int y)
3024 cairo_t *cr = cairo_create (csBoardWindow);
3026 cairo_select_font_face (cr, "Sans",
3027 CAIRO_FONT_SLANT_NORMAL,
3028 CAIRO_FONT_WEIGHT_NORMAL);
3030 cairo_set_font_size (cr, 12.0);
3032 cairo_move_to (cr, x, y+4);
3033 cairo_show_text( cr, buf);
3035 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
3042 void DrawSeekDot(int x, int y, int colorNr)
3044 cairo_t *cr = cairo_create (csBoardWindow);
3045 int square = colorNr & 0x80;
3049 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3051 cairo_arc(cr, x, y, squareSize/8, 0.0, 2*M_PI);
3053 SetPen(cr, 2, "#000000", 0);
3054 cairo_stroke_preserve(cr);
3056 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
3057 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
3058 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
3069 int boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3070 int boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3071 if(!csBoardWindow) {
3072 csBoardWindow = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), boardWidth, boardHeight);
3073 csBoardBackup = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, boardWidth, boardHeight);
3083 DoDrawGrid(cairo_surface_t *cs)
3085 /* draws a grid starting around Nx, Ny squares starting at x,y */
3091 cr = cairo_create (cs);
3093 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
3094 SetPen(cr, lineGap, "#000000", 0);
3097 for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
3099 cairo_move_to (cr, gridSegments[i].x1, gridSegments[i].y1);
3100 cairo_line_to (cr, gridSegments[i].x2, gridSegments[i].y2);
3113 DoDrawGrid(csBoardWindow);
3114 DoDrawGrid(csBoardBackup);
3118 * event handler for redrawing the board
3121 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3123 DrawPosition(True, NULL);
3128 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3129 { // [HGM] pv: walk PV
3130 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3133 static int savedIndex; /* gross that this is global */
3136 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3139 XawTextPosition index, dummy;
3142 XawTextGetSelectionPos(w, &index, &dummy);
3143 XtSetArg(arg, XtNstring, &val);
3144 XtGetValues(w, &arg, 1);
3145 ReplaceComment(savedIndex, val);
3146 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3147 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3151 EditCommentPopUp (int index, char *title, char *text)
3154 if (text == NULL) text = "";
3155 NewCommentPopup(title, text, index);
3159 CommentPopUp (char *title, char *text)
3161 savedIndex = currentMove; // [HGM] vari
3162 NewCommentPopup(title, text, currentMove);
3168 PopDown(CommentDlg);
3172 /* Disable all user input other than deleting the window */
3173 static int frozen = 0;
3179 /* Grab by a widget that doesn't accept input */
3180 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
3184 /* Undo a FreezeUI */
3188 if (!frozen) return;
3189 XtRemoveGrab(optList[W_MESSG].handle);
3197 static int oldPausing = FALSE;
3198 static GameMode oldmode = (GameMode) -1;
3201 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3203 if (pausing != oldPausing) {
3204 oldPausing = pausing;
3205 MarkMenuItem("Mode.Pause", pausing);
3207 if (appData.showButtonBar) {
3208 /* Always toggle, don't set. Previous code messes up when
3209 invoked while the button is pressed, as releasing it
3210 toggles the state again. */
3213 XtSetArg(args[0], XtNbackground, &oldbg);
3214 XtSetArg(args[1], XtNforeground, &oldfg);
3215 XtGetValues(optList[W_PAUSE].handle,
3217 XtSetArg(args[0], XtNbackground, oldfg);
3218 XtSetArg(args[1], XtNforeground, oldbg);
3220 XtSetValues(optList[W_PAUSE].handle, args, 2);
3224 wname = ModeToWidgetName(oldmode);
3225 if (wname != NULL) {
3226 MarkMenuItem(wname, False);
3228 wname = ModeToWidgetName(gameMode);
3229 if (wname != NULL) {
3230 MarkMenuItem(wname, True);
3233 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
3235 /* Maybe all the enables should be handled here, not just this one */
3236 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
3238 DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
3243 * Button/menu procedures
3246 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3247 char *selected_fen_position=NULL;
3250 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3251 Atom *type_return, XtPointer *value_return,
3252 unsigned long *length_return, int *format_return)
3254 char *selection_tmp;
3256 // if (!selected_fen_position) return False; /* should never happen */
3257 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3258 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3259 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3262 if (f == NULL) return False;
3266 selection_tmp = XtMalloc(len + 1);
3267 count = fread(selection_tmp, 1, len, f);
3270 XtFree(selection_tmp);
3273 selection_tmp[len] = NULLCHAR;
3275 /* note: since no XtSelectionDoneProc was registered, Xt will
3276 * automatically call XtFree on the value returned. So have to
3277 * make a copy of it allocated with XtMalloc */
3278 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3279 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3282 *value_return=selection_tmp;
3283 *length_return=strlen(selection_tmp);
3284 *type_return=*target;
3285 *format_return = 8; /* bits per byte */
3287 } else if (*target == XA_TARGETS(xDisplay)) {
3288 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3289 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3290 targets_tmp[1] = XA_STRING;
3291 *value_return = targets_tmp;
3292 *type_return = XA_ATOM;
3295 // This code leads to a read of value_return out of bounds on 64-bit systems.
3296 // Other code which I have seen always sets *format_return to 32 independent of
3297 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3298 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3299 *format_return = 8 * sizeof(Atom);
3300 if (*format_return > 32) {
3301 *length_return *= *format_return / 32;
3302 *format_return = 32;
3305 *format_return = 32;
3313 /* note: when called from menu all parameters are NULL, so no clue what the
3314 * Widget which was clicked on was, or what the click event was
3317 CopySomething (char *src)
3319 selected_fen_position = src;
3321 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3322 * have a notion of a position that is selected but not copied.
3323 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3325 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3327 SendPositionSelection,
3328 NULL/* lose_ownership_proc */ ,
3329 NULL/* transfer_done_proc */);
3330 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3332 SendPositionSelection,
3333 NULL/* lose_ownership_proc */ ,
3334 NULL/* transfer_done_proc */);
3337 /* function called when the data to Paste is ready */
3339 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3340 Atom *type, XtPointer value, unsigned long *len, int *format)
3343 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3344 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3345 EditPositionPasteFEN(fenstr);
3349 /* called when Paste Position button is pressed,
3350 * all parameters will be NULL */
3352 PastePositionProc ()
3354 XtGetSelectionValue(menuBarWidget,
3355 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3356 /* (XtSelectionCallbackProc) */ PastePositionCB,
3357 NULL, /* client_data passed to PastePositionCB */
3359 /* better to use the time field from the event that triggered the
3360 * call to this function, but that isn't trivial to get
3367 /* note: when called from menu all parameters are NULL, so no clue what the
3368 * Widget which was clicked on was, or what the click event was
3370 /* function called when the data to Paste is ready */
3372 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3373 Atom *type, XtPointer value, unsigned long *len, int *format)
3376 if (value == NULL || *len == 0) {
3377 return; /* nothing had been selected to copy */
3379 f = fopen(gamePasteFilename, "w");
3381 DisplayError(_("Can't open temp file"), errno);
3384 fwrite(value, 1, *len, f);
3387 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3390 /* called when Paste Game button is pressed,
3391 * all parameters will be NULL */
3395 XtGetSelectionValue(menuBarWidget,
3396 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3397 /* (XtSelectionCallbackProc) */ PasteGameCB,
3398 NULL, /* client_data passed to PasteGameCB */
3400 /* better to use the time field from the event that triggered the
3401 * call to this function, but that isn't trivial to get
3410 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3417 { // bassic primitive for determining if modifier keys are pressed
3418 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3421 XQueryKeymap(xDisplay,keys);
3422 for(i=0; i<6; i++) {
3424 j = XKeysymToKeycode(xDisplay, codes[i]);
3425 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3431 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3435 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3436 if ( n == 1 && *buf >= 32 // printable
3437 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3438 ) BoxAutoPopUp (buf);
3442 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3443 { // [HGM] input: let up-arrow recall previous line from history
3448 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3449 { // [HGM] input: let down-arrow recall next line from history
3454 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3460 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3462 if (!TempBackwardActive) {
3463 TempBackwardActive = True;
3469 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3471 /* Check to see if triggered by a key release event for a repeating key.
3472 * If so the next queued event will be a key press of the same key at the same time */
3473 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3475 XPeekEvent(xDisplay, &next);
3476 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3477 next.xkey.keycode == event->xkey.keycode)
3481 TempBackwardActive = False;
3485 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3486 { // called as key binding
3489 if (nprms && *nprms > 0)
3493 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3499 { // called from menu
3500 ManInner(NULL, NULL, NULL, NULL);
3504 SetWindowTitle (char *text, char *title, char *icon)
3508 if (appData.titleInWindow) {
3510 XtSetArg(args[i], XtNlabel, text); i++;
3511 XtSetValues(titleWidget, args, i);
3514 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3515 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3516 XtSetValues(shellWidget, args, i);
3517 XSync(xDisplay, False);
3522 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3528 DisplayIcsInteractionTitle (String message)
3530 if (oldICSInteractionTitle == NULL) {
3531 /* Magic to find the old window title, adapted from vim */
3532 char *wina = getenv("WINDOWID");
3534 Window win = (Window) atoi(wina);
3535 Window root, parent, *children;
3536 unsigned int nchildren;
3537 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3539 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3540 if (!XQueryTree(xDisplay, win, &root, &parent,
3541 &children, &nchildren)) break;
3542 if (children) XFree((void *)children);
3543 if (parent == root || parent == 0) break;
3546 XSetErrorHandler(oldHandler);
3548 if (oldICSInteractionTitle == NULL) {
3549 oldICSInteractionTitle = "xterm";
3552 printf("\033]0;%s\007", message);
3557 XtIntervalId delayedEventTimerXID = 0;
3558 DelayedEventCallback delayedEventCallback = 0;
3563 delayedEventTimerXID = 0;
3564 delayedEventCallback();
3568 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3570 if(delayedEventTimerXID && delayedEventCallback == cb)
3571 // [HGM] alive: replace, rather than add or flush identical event
3572 XtRemoveTimeOut(delayedEventTimerXID);
3573 delayedEventCallback = cb;
3574 delayedEventTimerXID =
3575 XtAppAddTimeOut(appContext, millisec,
3576 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3579 DelayedEventCallback
3582 if (delayedEventTimerXID) {
3583 return delayedEventCallback;
3590 CancelDelayedEvent ()
3592 if (delayedEventTimerXID) {
3593 XtRemoveTimeOut(delayedEventTimerXID);
3594 delayedEventTimerXID = 0;
3598 XtIntervalId loadGameTimerXID = 0;
3601 LoadGameTimerRunning ()
3603 return loadGameTimerXID != 0;
3607 StopLoadGameTimer ()
3609 if (loadGameTimerXID != 0) {
3610 XtRemoveTimeOut(loadGameTimerXID);
3611 loadGameTimerXID = 0;
3619 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3621 loadGameTimerXID = 0;
3626 StartLoadGameTimer (long millisec)
3629 XtAppAddTimeOut(appContext, millisec,
3630 (XtTimerCallbackProc) LoadGameTimerCallback,
3634 XtIntervalId analysisClockXID = 0;
3637 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3639 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3640 || appData.icsEngineAnalyze) { // [DM]
3641 AnalysisPeriodicEvent(0);
3642 StartAnalysisClock();
3647 StartAnalysisClock ()
3650 XtAppAddTimeOut(appContext, 2000,
3651 (XtTimerCallbackProc) AnalysisClockCallback,
3655 XtIntervalId clockTimerXID = 0;
3658 ClockTimerRunning ()
3660 return clockTimerXID != 0;
3666 if (clockTimerXID != 0) {
3667 XtRemoveTimeOut(clockTimerXID);
3676 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3683 StartClockTimer (long millisec)
3686 XtAppAddTimeOut(appContext, millisec,
3687 (XtTimerCallbackProc) ClockTimerCallback,
3692 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3696 Widget w = (Widget) opt->handle;
3698 /* check for low time warning */
3699 Pixel foregroundOrWarningColor = timerForegroundPixel;
3702 appData.lowTimeWarning &&
3703 (timer / 1000) < appData.icsAlarmTime)
3704 foregroundOrWarningColor = lowTimeWarningColor;
3706 if (appData.clockMode) {
3707 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
3708 XtSetArg(args[0], XtNlabel, buf);
3710 snprintf(buf, MSG_SIZ, "%s ", color);
3711 XtSetArg(args[0], XtNlabel, buf);
3716 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3717 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3719 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3720 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3723 XtSetValues(w, args, 3);
3726 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3729 SetClockIcon (int color)
3732 Pixmap pm = *clockIcons[color];
3733 if (iconPixmap != pm) {
3735 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3736 XtSetValues(shellWidget, args, 1);
3741 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3743 InputSource *is = (InputSource *) closure;
3748 if (is->lineByLine) {
3749 count = read(is->fd, is->unused,
3750 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3752 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3755 is->unused += count;
3757 while (p < is->unused) {
3758 q = memchr(p, '\n', is->unused - p);
3759 if (q == NULL) break;
3761 (is->func)(is, is->closure, p, q - p, 0);
3765 while (p < is->unused) {
3770 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3775 (is->func)(is, is->closure, is->buf, count, error);
3780 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3783 ChildProc *cp = (ChildProc *) pr;
3785 is = (InputSource *) calloc(1, sizeof(InputSource));
3786 is->lineByLine = lineByLine;
3790 is->fd = fileno(stdin);
3792 is->kind = cp->kind;
3793 is->fd = cp->fdFrom;
3796 is->unused = is->buf;
3799 is->xid = XtAppAddInput(appContext, is->fd,
3800 (XtPointer) (XtInputReadMask),
3801 (XtInputCallbackProc) DoInputCallback,
3803 is->closure = closure;
3804 return (InputSourceRef) is;
3808 RemoveInputSource (InputSourceRef isr)
3810 InputSource *is = (InputSource *) isr;
3812 if (is->xid == 0) return;
3813 XtRemoveInput(is->xid);
3817 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3819 /* Masks for XPM pieces. Black and white pieces can have
3820 different shapes, but in the interest of retaining my
3821 sanity pieces must have the same outline on both light
3822 and dark squares, and all pieces must use the same
3823 background square colors/images. */
3825 static int xpmDone = 0;
3826 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3827 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3828 static cairo_surface_t *c_animBufs[3*NrOfAnims]; // newBuf, saveBuf
3831 CreateAnimMasks (int pieceDepth)
3837 unsigned long plane;
3840 /* Need a bitmap just to get a GC with right depth */
3841 buf = XCreatePixmap(xDisplay, xBoardWindow,
3843 values.foreground = 1;
3844 values.background = 0;
3845 /* Don't use XtGetGC, not read only */
3846 maskGC = XCreateGC(xDisplay, buf,
3847 GCForeground | GCBackground, &values);
3848 XFreePixmap(xDisplay, buf);
3850 buf = XCreatePixmap(xDisplay, xBoardWindow,
3851 squareSize, squareSize, pieceDepth);
3852 values.foreground = XBlackPixel(xDisplay, xScreen);
3853 values.background = XWhitePixel(xDisplay, xScreen);
3854 bufGC = XCreateGC(xDisplay, buf,
3855 GCForeground | GCBackground, &values);
3857 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3858 /* Begin with empty mask */
3859 if(!xpmDone) // [HGM] pieces: keep using existing
3860 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3861 squareSize, squareSize, 1);
3862 XSetFunction(xDisplay, maskGC, GXclear);
3863 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3864 0, 0, squareSize, squareSize);
3866 /* Take a copy of the piece */
3871 XSetFunction(xDisplay, bufGC, GXcopy);
3872 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3874 0, 0, squareSize, squareSize, 0, 0);
3876 /* XOR the background (light) over the piece */
3877 XSetFunction(xDisplay, bufGC, GXxor);
3879 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3880 0, 0, squareSize, squareSize, 0, 0);
3882 XSetForeground(xDisplay, bufGC, lightSquareColor);
3883 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3886 /* We now have an inverted piece image with the background
3887 erased. Construct mask by just selecting all the non-zero
3888 pixels - no need to reconstruct the original image. */
3889 XSetFunction(xDisplay, maskGC, GXor);
3891 /* Might be quicker to download an XImage and create bitmap
3892 data from it rather than this N copies per piece, but it
3893 only takes a fraction of a second and there is a much
3894 longer delay for loading the pieces. */
3895 for (n = 0; n < pieceDepth; n ++) {
3896 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3897 0, 0, squareSize, squareSize,
3903 XFreePixmap(xDisplay, buf);
3904 XFreeGC(xDisplay, bufGC);
3905 XFreeGC(xDisplay, maskGC);
3909 InitAnimState (AnimNr anr, XWindowAttributes *info)
3915 DrawSeekOpen(); // set cs to board widget
3916 if(c_animBufs[anr]) cairo_surface_destroy (c_animBufs[anr]);
3917 if(c_animBufs[anr+2]) cairo_surface_destroy (c_animBufs[anr+2]);
3918 c_animBufs[anr+4] = csBoardWindow;
3919 c_animBufs[anr+2] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3920 c_animBufs[anr] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3923 /* Each buffer is square size, same depth as window */
3924 animBufs[anr+4] = xBoardWindow;
3925 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3926 squareSize, squareSize, info->depth);
3927 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3928 squareSize, squareSize, info->depth);
3930 /* Create a plain GC for blitting */
3931 mask = GCForeground | GCBackground | GCFunction |
3932 GCPlaneMask | GCGraphicsExposures;
3933 values.foreground = XBlackPixel(xDisplay, xScreen);
3934 values.background = XWhitePixel(xDisplay, xScreen);
3935 values.function = GXcopy;
3936 values.plane_mask = AllPlanes;
3937 values.graphics_exposures = False;
3938 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3940 /* Piece will be copied from an existing context at
3941 the start of each new animation/drag. */
3942 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3944 /* Outline will be a read-only copy of an existing */
3945 animGCs[anr+4] = None;
3951 XWindowAttributes info;
3953 if (!cairoAnimate && xpmDone && gameInfo.variant == oldVariant) return;
3954 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3955 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3957 InitAnimState(Game, &info);
3958 InitAnimState(Player, &info);
3960 /* For XPM pieces, we need bitmaps to use as masks. */
3961 if (useImages & !xpmDone)
3962 CreateAnimMasks(info.depth), xpmDone = 1;
3967 static Boolean frameWaiting;
3970 FrameAlarm (int sig)
3972 frameWaiting = False;
3973 /* In case System-V style signals. Needed?? */
3974 signal(SIGALRM, FrameAlarm);
3978 FrameDelay (int time)
3980 struct itimerval delay;
3982 XSync(xDisplay, False);
3985 frameWaiting = True;
3986 signal(SIGALRM, FrameAlarm);
3987 delay.it_interval.tv_sec =
3988 delay.it_value.tv_sec = time / 1000;
3989 delay.it_interval.tv_usec =
3990 delay.it_value.tv_usec = (time % 1000) * 1000;
3991 setitimer(ITIMER_REAL, &delay, NULL);
3992 while (frameWaiting) pause();
3993 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3994 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3995 setitimer(ITIMER_REAL, &delay, NULL);
4002 FrameDelay (int time)
4004 XSync(xDisplay, False);
4006 usleep(time * 1000);
4012 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
4016 /* Bitmap for piece being moved. */
4017 if (appData.monoMode) {
4018 *mask = *pieceToSolid(piece);
4019 } else if (useImages) {
4021 *mask = xpmMask[piece];
4023 *mask = ximMaskPm[piece];
4026 *mask = *pieceToSolid(piece);
4029 /* GC for piece being moved. Square color doesn't matter, but
4030 since it gets modified we make a copy of the original. */
4032 if (appData.monoMode)
4037 if (appData.monoMode)
4042 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
4044 /* Outline only used in mono mode and is not modified */
4046 *outline = bwPieceGC;
4048 *outline = wbPieceGC;
4052 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
4057 /* Draw solid rectangle which will be clipped to shape of piece */
4058 XFillRectangle(xDisplay, dest, clip,
4059 0, 0, squareSize, squareSize);
4060 if (appData.monoMode)
4061 /* Also draw outline in contrasting color for black
4062 on black / white on white cases */
4063 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
4064 0, 0, squareSize, squareSize, 0, 0, 1);
4066 /* Copy the piece */
4071 if(appData.upsideDown && flipView) kind ^= 2;
4072 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4074 0, 0, squareSize, squareSize,
4080 CairoOverlayPiece (ChessSquare piece, cairo_surface_t *dest)
4082 static ChessSquare oldPiece = -1;
4084 static cairo_t *pieceSource;
4085 if(piece != oldPiece || squareSize != oldSize) { // try make it faster by only changing cr if we need other piece
4086 if(pieceSource) cairo_destroy (pieceSource);
4087 pieceSource = cairo_create (dest);
4088 cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
4089 oldPiece = piece; oldSize = squareSize;
4091 cairo_paint(pieceSource);
4095 InsertPiece (AnimNr anr, ChessSquare piece)
4098 CairoOverlayPiece(piece, c_animBufs[anr]);
4100 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
4104 DrawBlank (AnimNr anr, int x, int y, int startColor)
4107 BlankSquare(x, y, startColor, EmptySquare, (Drawable) c_animBufs[anr+2], 0);
4109 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
4112 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
4113 int srcX, int srcY, int width, int height, int destX, int destY)
4116 cairo_t *cr;// = cairo_create (c_animBufs[anr+destBuf]);
4117 cr = cairo_create (c_animBufs[anr+destBuf]);
4118 if(c_animBufs[anr+srcBuf] == csBoardWindow)
4119 cairo_set_source_surface (cr, csBoardBackup, destX - srcX, destY - srcY);
4121 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
4122 cairo_rectangle (cr, destX, destY, width, height);
4125 if(c_animBufs[anr+destBuf] == csBoardWindow) {
4126 cr = cairo_create (csBoardBackup); // also draw to backup
4127 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
4128 cairo_rectangle (cr, destX, destY, width, height);
4133 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
4134 srcX, srcY, width, height, destX, destY);
4138 SetDragPiece (AnimNr anr, ChessSquare piece)
4141 if(cairoAnimate) return;
4142 /* The piece will be drawn using its own bitmap as a matte */
4143 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
4144 XSetClipMask(xDisplay, animGCs[anr+2], mask);
4147 /* [AS] Arrow highlighting support */
4149 void DrawPolygon(Pnt arrow[], int nr)
4150 { // for now on own surface; eventually this should become a global that is only destroyed on resize
4151 cairo_surface_t *boardSurface;
4154 int w = lineGap + BOARD_WIDTH * (squareSize + lineGap);
4155 int h = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
4156 boardSurface = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), w, h);
4157 cr = cairo_create (boardSurface);
4158 cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
4159 for (i=0;i<nr;i++) {
4160 cairo_line_to(cr, arrow[i].x, arrow[i].y);
4162 if(appData.monoMode) { // should we always outline arrow?
4163 cairo_line_to(cr, arrow[0].x, arrow[0].y);
4164 SetPen(cr, 2, "#000000", 0);
4165 cairo_stroke_preserve(cr);
4167 SetPen(cr, 2, appData.highlightSquareColor, 0);
4172 cairo_surface_destroy (boardSurface);
4176 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
4178 char buf[MSG_SIZ], *logoName = buf;
4179 if(appData.logo[n][0]) {
4180 logoName = appData.logo[n];
4181 } else if(appData.autoLogo) {
4182 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
4183 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
4184 } else if(appData.directory[n] && appData.directory[n][0]) {
4185 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
4189 { ASSIGN(cps->programLogo, logoName); }
4193 UpdateLogos (int displ)
4195 if(optList[W_WHITE-1].handle == NULL) return;
4196 LoadLogo(&first, 0, 0);
4197 LoadLogo(&second, 1, appData.icsActive);
4198 if(displ) DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);