2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
64 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.h>
68 # if HAVE_SYS_SOCKET_H
69 # include <sys/socket.h>
70 # include <netinet/in.h>
72 # else /* not HAVE_SYS_SOCKET_H */
73 # if HAVE_LAN_SOCKET_H
74 # include <lan/socket.h>
76 # include <lan/netdb.h>
77 # else /* not HAVE_LAN_SOCKET_H */
78 # define OMIT_SOCKETS 1
79 # endif /* not HAVE_LAN_SOCKET_H */
80 # endif /* not HAVE_SYS_SOCKET_H */
81 #endif /* !OMIT_SOCKETS */
86 #else /* not STDC_HEADERS */
87 extern char *getenv();
90 # else /* not HAVE_STRING_H */
92 # endif /* not HAVE_STRING_H */
93 #endif /* not STDC_HEADERS */
96 # include <sys/fcntl.h>
97 #else /* not HAVE_SYS_FCNTL_H */
100 # endif /* HAVE_FCNTL_H */
101 #endif /* not HAVE_SYS_FCNTL_H */
103 #if HAVE_SYS_SYSTEMINFO_H
104 # include <sys/systeminfo.h>
105 #endif /* HAVE_SYS_SYSTEMINFO_H */
107 #if TIME_WITH_SYS_TIME
108 # include <sys/time.h>
112 # include <sys/time.h>
123 # include <sys/wait.h>
128 # define NAMLEN(dirent) strlen((dirent)->d_name)
129 # define HAVE_DIR_STRUCT
131 # define dirent direct
132 # define NAMLEN(dirent) (dirent)->d_namlen
134 # include <sys/ndir.h>
135 # define HAVE_DIR_STRUCT
138 # include <sys/dir.h>
139 # define HAVE_DIR_STRUCT
143 # define HAVE_DIR_STRUCT
151 #include <X11/Intrinsic.h>
152 #include <X11/StringDefs.h>
153 #include <X11/Shell.h>
154 #include <X11/cursorfont.h>
155 #include <X11/Xatom.h>
156 #include <X11/Xmu/Atoms.h>
158 #include <X11/Xaw3d/Dialog.h>
159 #include <X11/Xaw3d/Form.h>
160 #include <X11/Xaw3d/List.h>
161 #include <X11/Xaw3d/Label.h>
162 #include <X11/Xaw3d/SimpleMenu.h>
163 #include <X11/Xaw3d/SmeBSB.h>
164 #include <X11/Xaw3d/SmeLine.h>
165 #include <X11/Xaw3d/Box.h>
166 #include <X11/Xaw3d/MenuButton.h>
167 #include <X11/Xaw3d/Text.h>
168 #include <X11/Xaw3d/AsciiText.h>
170 #include <X11/Xaw/Dialog.h>
171 #include <X11/Xaw/Form.h>
172 #include <X11/Xaw/List.h>
173 #include <X11/Xaw/Label.h>
174 #include <X11/Xaw/SimpleMenu.h>
175 #include <X11/Xaw/SmeBSB.h>
176 #include <X11/Xaw/SmeLine.h>
177 #include <X11/Xaw/Box.h>
178 #include <X11/Xaw/MenuButton.h>
179 #include <X11/Xaw/Text.h>
180 #include <X11/Xaw/AsciiText.h>
183 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
188 #include "pixmaps/pixmaps.h"
189 #define IMAGE_EXT "xpm"
191 #define IMAGE_EXT "xim"
192 #include "bitmaps/bitmaps.h"
195 #include "bitmaps/icon_white.bm"
196 #include "bitmaps/icon_black.bm"
197 #include "bitmaps/checkmark.bm"
199 #include "frontend.h"
201 #include "backendz.h"
205 #include "xgamelist.h"
206 #include "xhistory.h"
207 #include "xevalgraph.h"
208 #include "xedittags.h"
212 #include "engineoutput.h"
221 #define usleep(t) _sleep2(((t)+500)/1000)
225 # define _(s) gettext (s)
226 # define N_(s) gettext_noop (s)
232 int main P((int argc, char **argv));
233 RETSIGTYPE CmailSigHandler P((int sig));
234 RETSIGTYPE IntSigHandler P((int sig));
235 RETSIGTYPE TermSizeSigHandler P((int sig));
236 static void CreateGCs P((int redo));
237 static void CreateAnyPieces P((void));
238 void CreateXIMPieces P((void));
239 void CreateXPMPieces P((void));
240 void CreatePNGPieces P((void));
241 void CreateXPMBoard P((char *s, int n));
242 void CreatePieces P((void));
243 Widget CreateMenuBar P((Menu *mb, int boardWidth));
245 char *InsertPxlSize P((char *pattern, int targetPxlSize));
246 XFontSet CreateFontSet P((char *base_fnt_lst));
248 char *FindFont P((char *pattern, int targetPxlSize));
250 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
251 u_int wreq, u_int hreq));
252 void CreateGrid P((void));
253 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
254 void DelayedDrag P((void));
255 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
256 void HandlePV P((Widget w, XEvent * event,
257 String * params, Cardinal * nParams));
258 void DrawPositionProc P((Widget w, XEvent *event,
259 String *prms, Cardinal *nprms));
260 void CommentClick P((Widget w, XEvent * event,
261 String * params, Cardinal * nParams));
262 void ICSInputBoxPopUp P((void));
263 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
264 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
265 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
266 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
267 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
268 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
269 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
270 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
271 Boolean TempBackwardActive = False;
272 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
273 void DisplayMove P((int moveNumber));
274 void ICSInitScript P((void));
275 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
276 void update_ics_width P(());
277 int CopyMemoProc P(());
280 * XBoard depends on Xt R4 or higher
282 int xtVersion = XtSpecificationRelease;
287 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
288 highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
289 Pixel lowTimeWarningColor;
290 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
291 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
293 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
294 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
295 Option *optList; // contains all widgets of main window
296 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
298 XFontSet fontSet, clockFontSet;
301 XFontStruct *clockFontStruct;
303 Font coordFontID, countFontID;
304 XFontStruct *coordFontStruct, *countFontStruct;
305 XtAppContext appContext;
308 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
310 Position commentX = -1, commentY = -1;
311 Dimension commentW, commentH;
312 typedef unsigned int BoardSize;
314 Boolean chessProgram;
316 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
317 int smallLayout = 0, tinyLayout = 0,
318 marginW, marginH, // [HGM] for run-time resizing
319 fromX = -1, fromY = -1, toX, toY, commentUp = False,
320 errorExitStatus = -1, defaultLineGap;
321 Dimension textHeight;
322 Pixel timerForegroundPixel, timerBackgroundPixel;
323 Pixel buttonForegroundPixel, buttonBackgroundPixel;
324 char *chessDir, *programName, *programVersion;
325 Boolean alwaysOnTop = False;
326 char *icsTextMenuString;
328 char *firstChessProgramNames;
329 char *secondChessProgramNames;
331 WindowPlacement wpMain;
332 WindowPlacement wpConsole;
333 WindowPlacement wpComment;
334 WindowPlacement wpMoveHistory;
335 WindowPlacement wpEvalGraph;
336 WindowPlacement wpEngineOutput;
337 WindowPlacement wpGameList;
338 WindowPlacement wpTags;
343 Boolean cairoAnimate = True;
344 static cairo_surface_t *csBoardWindow, *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.
1536 shellArgs[0].value = (XtArgVal) &w;
1537 shellArgs[1].value = (XtArgVal) &h;
1538 XtGetValues(shellWidget, shellArgs, 2);
1539 shellArgs[4].value = shellArgs[2].value = w;
1540 shellArgs[5].value = shellArgs[3].value = h;
1541 if(!cairoAnimate) XtSetValues(shellWidget, &shellArgs[2], 4);
1542 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1543 marginH = h - boardHeight;
1545 CatchDeleteWindow(shellWidget, "QuitProc");
1551 if(appData.logoSize)
1552 { // locate and read user logo
1554 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1555 ASSIGN(userLogo, buf);
1558 if (appData.animate || appData.animateDragging)
1561 XtAugmentTranslations(formWidget,
1562 XtParseTranslationTable(globalTranslations));
1564 XtAddEventHandler(formWidget, KeyPressMask, False,
1565 (XtEventHandler) MoveTypeInProc, NULL);
1566 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1567 (XtEventHandler) EventProc, NULL);
1569 /* [AS] Restore layout */
1570 if( wpMoveHistory.visible ) {
1574 if( wpEvalGraph.visible )
1579 if( wpEngineOutput.visible ) {
1580 EngineOutputPopUp();
1585 if (errorExitStatus == -1) {
1586 if (appData.icsActive) {
1587 /* We now wait until we see "login:" from the ICS before
1588 sending the logon script (problems with timestamp otherwise) */
1589 /*ICSInitScript();*/
1590 if (appData.icsInputBox) ICSInputBoxPopUp();
1594 signal(SIGWINCH, TermSizeSigHandler);
1596 signal(SIGINT, IntSigHandler);
1597 signal(SIGTERM, IntSigHandler);
1598 if (*appData.cmailGameName != NULLCHAR) {
1599 signal(SIGUSR1, CmailSigHandler);
1603 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1606 // XtSetKeyboardFocus(shellWidget, formWidget);
1607 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1609 XtAppMainLoop(appContext);
1610 if (appData.debugMode) fclose(debugFP); // [DM] debug
1615 TermSizeSigHandler (int sig)
1621 IntSigHandler (int sig)
1627 CmailSigHandler (int sig)
1632 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1634 /* Activate call-back function CmailSigHandlerCallBack() */
1635 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1637 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1641 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1644 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1646 /**** end signal code ****/
1649 #define Abs(n) ((n)<0 ? -(n) : (n))
1653 InsertPxlSize (char *pattern, int targetPxlSize)
1655 char *base_fnt_lst, strInt[12], *p, *q;
1656 int alternatives, i, len, strIntLen;
1659 * Replace the "*" (if present) in the pixel-size slot of each
1660 * alternative with the targetPxlSize.
1664 while ((p = strchr(p, ',')) != NULL) {
1668 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1669 strIntLen = strlen(strInt);
1670 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1674 while (alternatives--) {
1675 char *comma = strchr(p, ',');
1676 for (i=0; i<14; i++) {
1677 char *hyphen = strchr(p, '-');
1679 if (comma && hyphen > comma) break;
1680 len = hyphen + 1 - p;
1681 if (i == 7 && *p == '*' && len == 2) {
1683 memcpy(q, strInt, strIntLen);
1693 len = comma + 1 - p;
1700 return base_fnt_lst;
1704 CreateFontSet (char *base_fnt_lst)
1707 char **missing_list;
1711 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1712 &missing_list, &missing_count, &def_string);
1713 if (appData.debugMode) {
1715 XFontStruct **font_struct_list;
1716 char **font_name_list;
1717 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1719 fprintf(debugFP, " got list %s, locale %s\n",
1720 XBaseFontNameListOfFontSet(fntSet),
1721 XLocaleOfFontSet(fntSet));
1722 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1723 for (i = 0; i < count; i++) {
1724 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1727 for (i = 0; i < missing_count; i++) {
1728 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1731 if (fntSet == NULL) {
1732 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1737 #else // not ENABLE_NLS
1739 * Find a font that matches "pattern" that is as close as
1740 * possible to the targetPxlSize. Prefer fonts that are k
1741 * pixels smaller to fonts that are k pixels larger. The
1742 * pattern must be in the X Consortium standard format,
1743 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1744 * The return value should be freed with XtFree when no
1748 FindFont (char *pattern, int targetPxlSize)
1750 char **fonts, *p, *best, *scalable, *scalableTail;
1751 int i, j, nfonts, minerr, err, pxlSize;
1753 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1755 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1756 programName, pattern);
1763 for (i=0; i<nfonts; i++) {
1766 if (*p != '-') continue;
1768 if (*p == NULLCHAR) break;
1769 if (*p++ == '-') j++;
1771 if (j < 7) continue;
1774 scalable = fonts[i];
1777 err = pxlSize - targetPxlSize;
1778 if (Abs(err) < Abs(minerr) ||
1779 (minerr > 0 && err < 0 && -err == minerr)) {
1785 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1786 /* If the error is too big and there is a scalable font,
1787 use the scalable font. */
1788 int headlen = scalableTail - scalable;
1789 p = (char *) XtMalloc(strlen(scalable) + 10);
1790 while (isdigit(*scalableTail)) scalableTail++;
1791 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1793 p = (char *) XtMalloc(strlen(best) + 2);
1794 safeStrCpy(p, best, strlen(best)+1 );
1796 if (appData.debugMode) {
1797 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1798 pattern, targetPxlSize, p);
1800 XFreeFontNames(fonts);
1807 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1808 // must be called before all non-first callse to CreateGCs()
1809 XtReleaseGC(shellWidget, highlineGC);
1810 XtReleaseGC(shellWidget, lightSquareGC);
1811 XtReleaseGC(shellWidget, darkSquareGC);
1812 XtReleaseGC(shellWidget, lineGC);
1813 if (appData.monoMode) {
1814 if (DefaultDepth(xDisplay, xScreen) == 1) {
1815 XtReleaseGC(shellWidget, wbPieceGC);
1817 XtReleaseGC(shellWidget, bwPieceGC);
1820 XtReleaseGC(shellWidget, prelineGC);
1821 XtReleaseGC(shellWidget, wdPieceGC);
1822 XtReleaseGC(shellWidget, wlPieceGC);
1823 XtReleaseGC(shellWidget, bdPieceGC);
1824 XtReleaseGC(shellWidget, blPieceGC);
1829 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1831 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1832 | GCBackground | GCFunction | GCPlaneMask;
1833 gc_values->foreground = foreground;
1834 gc_values->background = background;
1835 return XtGetGC(shellWidget, value_mask, gc_values);
1839 CreateGCs (int redo)
1841 XGCValues gc_values;
1843 Pixel white = XWhitePixel(xDisplay, xScreen);
1844 Pixel black = XBlackPixel(xDisplay, xScreen);
1846 gc_values.plane_mask = AllPlanes;
1847 gc_values.line_width = lineGap;
1848 gc_values.line_style = LineSolid;
1849 gc_values.function = GXcopy;
1852 DeleteGCs(); // called a second time; clean up old GCs first
1853 } else { // [HGM] grid and font GCs created on first call only
1854 coordGC = CreateOneGC(&gc_values, black, white);
1855 XSetFont(xDisplay, coordGC, coordFontID);
1857 // [HGM] make font for holdings counts (white on black)
1858 countGC = CreateOneGC(&gc_values, white, black);
1859 XSetFont(xDisplay, countGC, countFontID);
1861 lineGC = CreateOneGC(&gc_values, black, black);
1863 if (appData.monoMode) {
1865 highlineGC = CreateOneGC(&gc_values, white, white);
1866 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1867 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1869 if (DefaultDepth(xDisplay, xScreen) == 1) {
1870 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1871 gc_values.function = GXcopyInverted;
1872 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1873 gc_values.function = GXcopy;
1874 if (XBlackPixel(xDisplay, xScreen) == 1) {
1875 bwPieceGC = darkSquareGC;
1876 wbPieceGC = copyInvertedGC;
1878 bwPieceGC = copyInvertedGC;
1879 wbPieceGC = lightSquareGC;
1884 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1885 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1886 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1887 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1888 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1889 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1890 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1891 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1896 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1904 fp = fopen(filename, "rb");
1906 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1913 for (y=0; y<h; ++y) {
1914 for (x=0; x<h; ++x) {
1919 XPutPixel(xim, x, y, blackPieceColor);
1921 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1924 XPutPixel(xim, x, y, darkSquareColor);
1926 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1929 XPutPixel(xim, x, y, whitePieceColor);
1931 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1934 XPutPixel(xim, x, y, lightSquareColor);
1936 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1944 /* create Pixmap of piece */
1945 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1947 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1950 /* create Pixmap of clipmask
1951 Note: We assume the white/black pieces have the same
1952 outline, so we make only 6 masks. This is okay
1953 since the XPM clipmask routines do the same. */
1955 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1957 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1960 /* now create the 1-bit version */
1961 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1964 values.foreground = 1;
1965 values.background = 0;
1967 /* Don't use XtGetGC, not read only */
1968 maskGC = XCreateGC(xDisplay, *mask,
1969 GCForeground | GCBackground, &values);
1970 XCopyPlane(xDisplay, temp, *mask, maskGC,
1971 0, 0, squareSize, squareSize, 0, 0, 1);
1972 XFreePixmap(xDisplay, temp);
1977 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1985 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1990 /* The XSynchronize calls were copied from CreatePieces.
1991 Not sure if needed, but can't hurt */
1992 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1995 /* temp needed by loadXIM() */
1996 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1997 0, 0, ss, ss, AllPlanes, XYPixmap);
1999 if (strlen(appData.pixmapDirectory) == 0) {
2003 if (appData.monoMode) {
2004 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2008 fprintf(stderr, _("\nLoading XIMs...\n"));
2010 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2011 fprintf(stderr, "%d", piece+1);
2012 for (kind=0; kind<4; kind++) {
2013 fprintf(stderr, ".");
2014 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2015 ExpandPathName(appData.pixmapDirectory),
2016 piece <= (int) WhiteKing ? "" : "w",
2017 pieceBitmapNames[piece],
2019 ximPieceBitmap[kind][piece] =
2020 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2021 0, 0, ss, ss, AllPlanes, XYPixmap);
2022 if (appData.debugMode)
2023 fprintf(stderr, _("(File:%s:) "), buf);
2024 loadXIM(ximPieceBitmap[kind][piece],
2026 &(xpmPieceBitmap2[kind][piece]),
2027 &(ximMaskPm2[piece]));
2028 if(piece <= (int)WhiteKing)
2029 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2031 fprintf(stderr," ");
2033 /* Load light and dark squares */
2034 /* If the LSQ and DSQ pieces don't exist, we will
2035 draw them with solid squares. */
2036 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2037 if (access(buf, 0) != 0) {
2041 fprintf(stderr, _("light square "));
2043 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2044 0, 0, ss, ss, AllPlanes, XYPixmap);
2045 if (appData.debugMode)
2046 fprintf(stderr, _("(File:%s:) "), buf);
2048 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2049 fprintf(stderr, _("dark square "));
2050 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2051 ExpandPathName(appData.pixmapDirectory), ss);
2052 if (appData.debugMode)
2053 fprintf(stderr, _("(File:%s:) "), buf);
2055 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2056 0, 0, ss, ss, AllPlanes, XYPixmap);
2057 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2058 xpmJailSquare = xpmLightSquare;
2060 fprintf(stderr, _("Done.\n"));
2062 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2065 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2069 CreateXPMBoard (char *s, int kind)
2073 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2074 if(strstr(s, ".png")) {
2075 cairo_surface_t *img = cairo_image_surface_create_from_png (s);
2077 useTexture |= kind + 1; pngBoardBitmap[kind] = img;
2078 textureW[kind] = cairo_image_surface_get_width (img);
2079 textureH[kind] = cairo_image_surface_get_height (img);
2082 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2083 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2089 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2090 // thisroutine has to be called t free the old piece pixmaps
2092 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2093 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2095 XFreePixmap(xDisplay, xpmLightSquare);
2096 XFreePixmap(xDisplay, xpmDarkSquare);
2105 u_int ss = squareSize;
2107 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2108 XpmColorSymbol symbols[4];
2109 static int redo = False;
2111 if(redo) FreeXPMPieces(); else redo = 1;
2113 /* The XSynchronize calls were copied from CreatePieces.
2114 Not sure if needed, but can't hurt */
2115 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2117 /* Setup translations so piece colors match square colors */
2118 symbols[0].name = "light_piece";
2119 symbols[0].value = appData.whitePieceColor;
2120 symbols[1].name = "dark_piece";
2121 symbols[1].value = appData.blackPieceColor;
2122 symbols[2].name = "light_square";
2123 symbols[2].value = appData.lightSquareColor;
2124 symbols[3].name = "dark_square";
2125 symbols[3].value = appData.darkSquareColor;
2127 attr.valuemask = XpmColorSymbols;
2128 attr.colorsymbols = symbols;
2129 attr.numsymbols = 4;
2131 if (appData.monoMode) {
2132 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2136 if (strlen(appData.pixmapDirectory) == 0) {
2137 XpmPieces* pieces = builtInXpms;
2140 while (pieces->size != squareSize && pieces->size) pieces++;
2141 if (!pieces->size) {
2142 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2145 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2146 for (kind=0; kind<4; kind++) {
2148 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2149 pieces->xpm[piece][kind],
2150 &(xpmPieceBitmap2[kind][piece]),
2151 NULL, &attr)) != 0) {
2152 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2156 if(piece <= (int) WhiteKing)
2157 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2161 xpmJailSquare = xpmLightSquare;
2165 fprintf(stderr, _("\nLoading XPMs...\n"));
2168 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2169 fprintf(stderr, "%d ", piece+1);
2170 for (kind=0; kind<4; kind++) {
2171 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2172 ExpandPathName(appData.pixmapDirectory),
2173 piece > (int) WhiteKing ? "w" : "",
2174 pieceBitmapNames[piece],
2176 if (appData.debugMode) {
2177 fprintf(stderr, _("(File:%s:) "), buf);
2179 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2180 &(xpmPieceBitmap2[kind][piece]),
2181 NULL, &attr)) != 0) {
2182 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2183 // [HGM] missing: read of unorthodox piece failed; substitute King.
2184 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2185 ExpandPathName(appData.pixmapDirectory),
2187 if (appData.debugMode) {
2188 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2190 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2191 &(xpmPieceBitmap2[kind][piece]),
2195 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2200 if(piece <= (int) WhiteKing)
2201 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2204 /* Load light and dark squares */
2205 /* If the LSQ and DSQ pieces don't exist, we will
2206 draw them with solid squares. */
2207 fprintf(stderr, _("light square "));
2208 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2209 if (access(buf, 0) != 0) {
2213 if (appData.debugMode)
2214 fprintf(stderr, _("(File:%s:) "), buf);
2216 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2217 &xpmLightSquare, NULL, &attr)) != 0) {
2218 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2221 fprintf(stderr, _("dark square "));
2222 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2223 ExpandPathName(appData.pixmapDirectory), ss);
2224 if (appData.debugMode) {
2225 fprintf(stderr, _("(File:%s:) "), buf);
2227 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2228 &xpmDarkSquare, NULL, &attr)) != 0) {
2229 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2233 xpmJailSquare = xpmLightSquare;
2234 fprintf(stderr, _("Done.\n"));
2236 oldVariant = -1; // kludge to force re-makig of animation masks
2237 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2240 #endif /* HAVE_LIBXPM */
2242 char *pngPieceNames[] = // must be in same order as internal piece encoding
2243 { "Pawn", "Knight", "Bishop", "Rook", "Queen", "Advisor", "Elephant", "Archbishop", "Marshall", "Gold", "Commoner",
2244 "Canon", "Nightrider", "CrownedBishop", "CrownedRook", "Princess", "Chancellor", "Hawk", "Lance", "Cobra", "Unicorn", "King",
2245 "GoldKnight", "GoldLance", "GoldPawn", "GoldSilver", NULL
2249 ScaleOnePiece (char *name, int color, int piece)
2253 cairo_surface_t *img, *cs;
2255 static cairo_surface_t *pngPieceImages[2][(int)BlackPawn+4]; // png 256 x 256 images
2257 if((img = pngPieceImages[color][piece]) == NULL) { // if PNG file for this piece was not yet read, read it now and store it
2258 snprintf(buf, MSG_SIZ, "%s/%s%s.png", appData.pngDirectory, color ? "Black" : "White", pngPieceNames[piece]);
2259 pngPieceImages[color][piece] = img = cairo_image_surface_create_from_png (buf);
2260 w = cairo_image_surface_get_width (img);
2261 h = cairo_image_surface_get_height (img);
2262 if(w != 64 || h != 64) { printf("Bad png size %dx%d in %s\n", w, h, buf); exit(1); }
2264 // create new bitmap to hold scaled piece image (and remove any old)
2265 if(pngPieceBitmaps2[color][piece]) cairo_surface_destroy (pngPieceBitmaps2[color][piece]);
2266 pngPieceBitmaps2[color][piece] = cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
2267 if(piece <= WhiteKing) pngPieceBitmaps[color][piece] = cs;
2268 // scaled copying of the raw png image
2269 cr = cairo_create(cs);
2270 cairo_scale(cr, squareSize/64., squareSize/64.);
2271 cairo_set_source_surface (cr, img, 0, 0);
2281 for(p=0; pngPieceNames[p]; p++) {
2282 ScaleOnePiece(pngPieceNames[p], 0, p);
2283 ScaleOnePiece(pngPieceNames[p], 1, p);
2288 /* No built-in bitmaps */
2293 u_int ss = squareSize;
2295 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2298 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2299 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2300 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2301 pieceBitmapNames[piece],
2302 ss, kind == SOLID ? 's' : 'o');
2303 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2304 if(piece <= (int)WhiteKing)
2305 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2309 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2313 /* With built-in bitmaps */
2317 BuiltInBits* bib = builtInBits;
2320 u_int ss = squareSize;
2322 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2325 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2327 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2328 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2329 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2330 pieceBitmapNames[piece],
2331 ss, kind == SOLID ? 's' : 'o');
2332 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2333 bib->bits[kind][piece], ss, ss);
2334 if(piece <= (int)WhiteKing)
2335 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2339 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2345 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2350 char msg[MSG_SIZ], fullname[MSG_SIZ];
2352 if (*appData.bitmapDirectory != NULLCHAR) {
2353 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2354 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2355 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2356 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2357 &w, &h, pm, &x_hot, &y_hot);
2358 fprintf(stderr, "load %s\n", name);
2359 if (errcode != BitmapSuccess) {
2361 case BitmapOpenFailed:
2362 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2364 case BitmapFileInvalid:
2365 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2367 case BitmapNoMemory:
2368 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2372 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2376 fprintf(stderr, _("%s: %s...using built-in\n"),
2378 } else if (w != wreq || h != hreq) {
2380 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2381 programName, fullname, w, h, wreq, hreq);
2387 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2397 if (lineGap == 0) return;
2399 /* [HR] Split this into 2 loops for non-square boards. */
2401 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2402 gridSegments[i].x1 = 0;
2403 gridSegments[i].x2 =
2404 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2405 gridSegments[i].y1 = gridSegments[i].y2
2406 = lineGap / 2 + (i * (squareSize + lineGap));
2409 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2410 gridSegments[j + i].y1 = 0;
2411 gridSegments[j + i].y2 =
2412 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2413 gridSegments[j + i].x1 = gridSegments[j + i].x2
2414 = lineGap / 2 + (j * (squareSize + lineGap));
2419 MarkMenuItem (char *menuRef, int state)
2421 MenuItem *item = MenuNameToItem(menuRef);
2425 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2426 XtSetValues(item->handle, args, 1);
2431 EnableNamedMenuItem (char *menuRef, int state)
2433 MenuItem *item = MenuNameToItem(menuRef);
2435 if(item) XtSetSensitive(item->handle, state);
2439 EnableButtonBar (int state)
2441 XtSetSensitive(optList[W_BUTTON].handle, state);
2446 SetMenuEnables (Enables *enab)
2448 while (enab->name != NULL) {
2449 EnableNamedMenuItem(enab->name, enab->value);
2455 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2456 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2458 if(*nprms == 0) return;
2459 item = MenuNameToItem(prms[0]);
2460 if(item) ((MenuProc *) item->proc) ();
2464 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2466 RecentEngineEvent((int) (intptr_t) addr);
2470 AppendMenuItem (char *msg, int n)
2472 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2484 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2485 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2486 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2487 dmEnables[i].piece);
2488 XtSetSensitive(entry, p != NULL || !appData.testLegality
2489 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2490 && !appData.icsActive));
2492 while (p && *p++ == dmEnables[i].piece) count++;
2493 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2495 XtSetArg(args[j], XtNlabel, label); j++;
2496 XtSetValues(entry, args, j);
2502 do_flash_delay (unsigned long msec)
2508 DoDrawBorder (cairo_surface_t *cs, int x, int y, int type)
2513 cr = cairo_create(cs);
2514 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
2515 cairo_rectangle(cr, x, y, squareSize+lineGap, squareSize+lineGap);
2516 SetPen(cr, lineGap, type == 1 ? appData.highlightSquareColor : appData.premoveHighlightColor, 0);
2521 DrawBorder (int x, int y, int type)
2523 DoDrawBorder(csBoardWindow, x, y, type);
2524 DoDrawBorder(csBoardBackup, x, y, type);
2528 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2530 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2531 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2533 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2534 if(textureW[kind] < W*squareSize)
2535 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2537 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2538 if(textureH[kind] < H*squareSize)
2539 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2541 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2546 DrawLogo (void *handle, void *logo)
2548 cairo_surface_t *img, *cs;
2552 if(!logo || !handle) return;
2553 cs = cairo_xlib_surface_create(xDisplay, XtWindow(handle), DefaultVisual(xDisplay, 0), appData.logoSize, appData.logoSize/2);
2554 img = cairo_image_surface_create_from_png (logo);
2555 w = cairo_image_surface_get_width (img);
2556 h = cairo_image_surface_get_height (img);
2557 cr = cairo_create(cs);
2558 cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
2559 cairo_set_source_surface (cr, img, 0, 0);
2562 cairo_surface_destroy (img);
2563 cairo_surface_destroy (cs);
2567 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2568 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2570 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2571 if(pngBoardBitmap[color]) {
2573 if(!fac && !cairoAnimate) return;
2575 cr = cairo_create (fac ? csBoardWindow : (cairo_surface_t *) dest);
2576 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2577 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2578 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2582 cr = cairo_create (csBoardBackup);
2583 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2584 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2585 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2590 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2591 squareSize, squareSize, x*fac, y*fac);
2593 if (useImages && useImageSqs) {
2597 pm = xpmLightSquare;
2602 case 2: /* neutral */
2604 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2607 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2608 squareSize, squareSize, x*fac, y*fac);
2618 case 2: /* neutral */
2623 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2628 I split out the routines to draw a piece so that I could
2629 make a generic flash routine.
2632 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2634 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2635 switch (square_color) {
2637 case 2: /* neutral */
2639 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2640 ? *pieceToOutline(piece)
2641 : *pieceToSolid(piece),
2642 dest, bwPieceGC, 0, 0,
2643 squareSize, squareSize, x, y);
2646 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2647 ? *pieceToSolid(piece)
2648 : *pieceToOutline(piece),
2649 dest, wbPieceGC, 0, 0,
2650 squareSize, squareSize, x, y);
2656 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2658 switch (square_color) {
2660 case 2: /* neutral */
2662 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2663 ? *pieceToOutline(piece)
2664 : *pieceToSolid(piece),
2665 dest, bwPieceGC, 0, 0,
2666 squareSize, squareSize, x, y, 1);
2669 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2670 ? *pieceToSolid(piece)
2671 : *pieceToOutline(piece),
2672 dest, wbPieceGC, 0, 0,
2673 squareSize, squareSize, x, y, 1);
2679 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2681 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2682 switch (square_color) {
2684 XCopyPlane(xDisplay, *pieceToSolid(piece),
2685 dest, (int) piece < (int) BlackPawn
2686 ? wlPieceGC : blPieceGC, 0, 0,
2687 squareSize, squareSize, x, y, 1);
2690 XCopyPlane(xDisplay, *pieceToSolid(piece),
2691 dest, (int) piece < (int) BlackPawn
2692 ? wdPieceGC : bdPieceGC, 0, 0,
2693 squareSize, squareSize, x, y, 1);
2695 case 2: /* neutral */
2697 break; // should never contain pieces
2702 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2704 int kind, p = piece;
2706 switch (square_color) {
2708 case 2: /* neutral */
2710 if ((int)piece < (int) BlackPawn) {
2718 if ((int)piece < (int) BlackPawn) {
2726 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2727 if(useTexture & square_color+1) {
2728 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2729 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2730 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2731 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2732 XSetClipMask(xDisplay, wlPieceGC, None);
2733 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2735 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2736 dest, wlPieceGC, 0, 0,
2737 squareSize, squareSize, x, y);
2741 pngDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2743 int kind, p = piece;
2746 if ((int)piece < (int) BlackPawn) {
2752 if(appData.upsideDown && flipView) { p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2753 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2755 cr = cairo_create (csBoardWindow);
2756 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2759 cr = cairo_create (csBoardBackup);
2760 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2765 typedef void (*DrawFunc)();
2770 if (appData.monoMode) {
2771 if (DefaultDepth(xDisplay, xScreen) == 1) {
2772 return monoDrawPiece_1bit;
2774 return monoDrawPiece;
2776 } else if(appData.pngDirectory[0]) {
2777 return pngDrawPiece;
2780 return colorDrawPieceImage;
2782 return colorDrawPiece;
2787 DoDrawDot (int marker, int x, int y, int r, cairo_surface_t *cs)
2791 cr = cairo_create(cs);
2792 cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
2793 if(appData.monoMode) {
2794 SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
2795 cairo_stroke_preserve(cr);
2796 SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
2798 SetPen(cr, 2, marker == 2 ? "#FF0000" : "#FFFF00", 0);
2807 DrawDot (int marker, int x, int y, int r)
2809 DoDrawDot(marker, x, y, r, csBoardWindow);
2810 DoDrawDot(marker, x, y, r, csBoardBackup);
2814 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2815 { // basic front-end board-draw function: takes care of everything that can be in square:
2816 // piece, background, coordinate/count, marker dot
2817 int direction, font_ascent, font_descent;
2818 XCharStruct overall;
2821 if (piece == EmptySquare) {
2822 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2824 drawfunc = ChooseDrawFunc();
2825 drawfunc(piece, square_color, x, y, xBoardWindow);
2828 if(align) { // square carries inscription (coord or piece count)
2830 GC hGC = align < 3 ? coordGC : countGC;
2831 // first calculate where it goes
2832 XTextExtents(countFontStruct, string, 1, &direction,
2833 &font_ascent, &font_descent, &overall);
2835 xx += squareSize - overall.width - 2;
2836 yy += squareSize - font_descent - 1;
2837 } else if (align == 2) {
2838 xx += 2, yy += font_ascent + 1;
2839 } else if (align == 3) {
2840 xx += squareSize - overall.width - 2;
2841 yy += font_ascent + 1;
2842 } else if (align == 4) {
2843 xx += 2, yy += font_ascent + 1;
2846 if (appData.monoMode) {
2847 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2849 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2853 if(marker) { // print fat marker dot, if requested
2854 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2859 FlashDelay (int flash_delay)
2861 XSync(xDisplay, False);
2862 if(flash_delay) do_flash_delay(flash_delay);
2866 Fraction (int x, int start, int stop)
2868 double f = ((double) x - start)/(stop - start);
2869 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2873 static WindowPlacement wpNew;
2876 CoDrag (Widget sh, WindowPlacement *wp)
2879 int j=0, touch=0, fudge = 2;
2880 GetActualPlacement(sh, wp);
2881 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2882 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2883 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2884 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2885 if(!touch ) return; // only windows that touch co-move
2886 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2887 int heightInc = wpNew.height - wpMain.height;
2888 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2889 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2890 wp->y += fracTop * heightInc;
2891 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2892 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2893 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2894 int widthInc = wpNew.width - wpMain.width;
2895 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2896 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2897 wp->y += fracLeft * widthInc;
2898 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2899 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2901 wp->x += wpNew.x - wpMain.x;
2902 wp->y += wpNew.y - wpMain.y;
2903 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2904 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2905 XtSetArg(args[j], XtNx, wp->x); j++;
2906 XtSetArg(args[j], XtNy, wp->y); j++;
2907 XtSetValues(sh, args, j);
2911 ReSize (WindowPlacement *wp)
2914 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
2915 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
2916 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
2917 if(sqy < sqx) sqx = sqy;
2918 if(sqx != squareSize) {
2919 squareSize = sqx; // adopt new square size
2921 CreatePNGPieces(); // make newly scaled pieces
2922 InitDrawingSizes(0, 0); // creates grid etc.
2926 static XtIntervalId delayedDragID = 0;
2935 GetActualPlacement(shellWidget, &wpNew);
2936 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2937 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
2938 busy = 0; return; // false alarm
2941 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2942 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2943 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2944 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2946 DrawPosition(True, NULL);
2947 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2955 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2957 XtAppAddTimeOut(appContext, 100, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2961 EventProc (Widget widget, caddr_t unused, XEvent *event)
2963 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2964 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2967 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
2970 Color (char *col, int n)
2973 sscanf(col, "#%x", &c);
2979 SetPen (cairo_t *cr, float w, char *col, int dash)
2981 static const double dotted[] = {4.0, 4.0};
2982 static int len = sizeof(dotted) / sizeof(dotted[0]);
2983 cairo_set_line_width (cr, w);
2984 cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
2985 if(dash) cairo_set_dash (cr, dotted, len, 0.0);
2988 void DrawSeekAxis( int x, int y, int xTo, int yTo )
2993 cr = cairo_create (csBoardWindow);
2995 cairo_move_to (cr, x, y);
2996 cairo_line_to(cr, xTo, yTo );
2998 SetPen(cr, 2, "#000000", 0);
3005 void DrawSeekBackground( int left, int top, int right, int bottom )
3007 cairo_t *cr = cairo_create (csBoardWindow);
3009 cairo_rectangle (cr, left, top, right-left, bottom-top);
3011 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
3018 void DrawSeekText(char *buf, int x, int y)
3020 cairo_t *cr = cairo_create (csBoardWindow);
3022 cairo_select_font_face (cr, "Sans",
3023 CAIRO_FONT_SLANT_NORMAL,
3024 CAIRO_FONT_WEIGHT_NORMAL);
3026 cairo_set_font_size (cr, 12.0);
3028 cairo_move_to (cr, x, y+4);
3029 cairo_show_text( cr, buf);
3031 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
3038 void DrawSeekDot(int x, int y, int colorNr)
3040 cairo_t *cr = cairo_create (csBoardWindow);
3041 int square = colorNr & 0x80;
3045 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3047 cairo_arc(cr, x, y, squareSize/8, 0.0, 2*M_PI);
3049 SetPen(cr, 2, "#000000", 0);
3050 cairo_stroke_preserve(cr);
3052 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
3053 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
3054 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
3065 int boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3066 int boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3067 if(!csBoardWindow) {
3068 csBoardWindow = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), boardWidth, boardHeight);
3069 csBoardBackup = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, boardWidth, boardHeight);
3079 DoDrawGrid(cairo_surface_t *cs)
3081 /* draws a grid starting around Nx, Ny squares starting at x,y */
3087 cr = cairo_create (cs);
3089 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
3090 SetPen(cr, lineGap, "#000000", 0);
3093 for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
3095 cairo_move_to (cr, gridSegments[i].x1, gridSegments[i].y1);
3096 cairo_line_to (cr, gridSegments[i].x2, gridSegments[i].y2);
3109 DoDrawGrid(csBoardWindow);
3110 DoDrawGrid(csBoardBackup);
3114 * event handler for redrawing the board
3117 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3119 DrawPosition(True, NULL);
3124 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3125 { // [HGM] pv: walk PV
3126 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3129 static int savedIndex; /* gross that this is global */
3132 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3135 XawTextPosition index, dummy;
3138 XawTextGetSelectionPos(w, &index, &dummy);
3139 XtSetArg(arg, XtNstring, &val);
3140 XtGetValues(w, &arg, 1);
3141 ReplaceComment(savedIndex, val);
3142 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3143 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3147 EditCommentPopUp (int index, char *title, char *text)
3150 if (text == NULL) text = "";
3151 NewCommentPopup(title, text, index);
3155 CommentPopUp (char *title, char *text)
3157 savedIndex = currentMove; // [HGM] vari
3158 NewCommentPopup(title, text, currentMove);
3164 PopDown(CommentDlg);
3168 /* Disable all user input other than deleting the window */
3169 static int frozen = 0;
3175 /* Grab by a widget that doesn't accept input */
3176 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
3180 /* Undo a FreezeUI */
3184 if (!frozen) return;
3185 XtRemoveGrab(optList[W_MESSG].handle);
3193 static int oldPausing = FALSE;
3194 static GameMode oldmode = (GameMode) -1;
3197 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3199 if (pausing != oldPausing) {
3200 oldPausing = pausing;
3201 MarkMenuItem("Mode.Pause", pausing);
3203 if (appData.showButtonBar) {
3204 /* Always toggle, don't set. Previous code messes up when
3205 invoked while the button is pressed, as releasing it
3206 toggles the state again. */
3209 XtSetArg(args[0], XtNbackground, &oldbg);
3210 XtSetArg(args[1], XtNforeground, &oldfg);
3211 XtGetValues(optList[W_PAUSE].handle,
3213 XtSetArg(args[0], XtNbackground, oldfg);
3214 XtSetArg(args[1], XtNforeground, oldbg);
3216 XtSetValues(optList[W_PAUSE].handle, args, 2);
3220 wname = ModeToWidgetName(oldmode);
3221 if (wname != NULL) {
3222 MarkMenuItem(wname, False);
3224 wname = ModeToWidgetName(gameMode);
3225 if (wname != NULL) {
3226 MarkMenuItem(wname, True);
3229 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
3231 /* Maybe all the enables should be handled here, not just this one */
3232 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
3234 DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
3239 * Button/menu procedures
3242 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3243 char *selected_fen_position=NULL;
3246 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3247 Atom *type_return, XtPointer *value_return,
3248 unsigned long *length_return, int *format_return)
3250 char *selection_tmp;
3252 // if (!selected_fen_position) return False; /* should never happen */
3253 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3254 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3255 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3258 if (f == NULL) return False;
3262 selection_tmp = XtMalloc(len + 1);
3263 count = fread(selection_tmp, 1, len, f);
3266 XtFree(selection_tmp);
3269 selection_tmp[len] = NULLCHAR;
3271 /* note: since no XtSelectionDoneProc was registered, Xt will
3272 * automatically call XtFree on the value returned. So have to
3273 * make a copy of it allocated with XtMalloc */
3274 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3275 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3278 *value_return=selection_tmp;
3279 *length_return=strlen(selection_tmp);
3280 *type_return=*target;
3281 *format_return = 8; /* bits per byte */
3283 } else if (*target == XA_TARGETS(xDisplay)) {
3284 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3285 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3286 targets_tmp[1] = XA_STRING;
3287 *value_return = targets_tmp;
3288 *type_return = XA_ATOM;
3291 // This code leads to a read of value_return out of bounds on 64-bit systems.
3292 // Other code which I have seen always sets *format_return to 32 independent of
3293 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3294 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3295 *format_return = 8 * sizeof(Atom);
3296 if (*format_return > 32) {
3297 *length_return *= *format_return / 32;
3298 *format_return = 32;
3301 *format_return = 32;
3309 /* note: when called from menu all parameters are NULL, so no clue what the
3310 * Widget which was clicked on was, or what the click event was
3313 CopySomething (char *src)
3315 selected_fen_position = src;
3317 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3318 * have a notion of a position that is selected but not copied.
3319 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3321 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3323 SendPositionSelection,
3324 NULL/* lose_ownership_proc */ ,
3325 NULL/* transfer_done_proc */);
3326 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3328 SendPositionSelection,
3329 NULL/* lose_ownership_proc */ ,
3330 NULL/* transfer_done_proc */);
3333 /* function called when the data to Paste is ready */
3335 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3336 Atom *type, XtPointer value, unsigned long *len, int *format)
3339 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3340 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3341 EditPositionPasteFEN(fenstr);
3345 /* called when Paste Position button is pressed,
3346 * all parameters will be NULL */
3348 PastePositionProc ()
3350 XtGetSelectionValue(menuBarWidget,
3351 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3352 /* (XtSelectionCallbackProc) */ PastePositionCB,
3353 NULL, /* client_data passed to PastePositionCB */
3355 /* better to use the time field from the event that triggered the
3356 * call to this function, but that isn't trivial to get
3363 /* note: when called from menu all parameters are NULL, so no clue what the
3364 * Widget which was clicked on was, or what the click event was
3366 /* function called when the data to Paste is ready */
3368 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3369 Atom *type, XtPointer value, unsigned long *len, int *format)
3372 if (value == NULL || *len == 0) {
3373 return; /* nothing had been selected to copy */
3375 f = fopen(gamePasteFilename, "w");
3377 DisplayError(_("Can't open temp file"), errno);
3380 fwrite(value, 1, *len, f);
3383 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3386 /* called when Paste Game button is pressed,
3387 * all parameters will be NULL */
3391 XtGetSelectionValue(menuBarWidget,
3392 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3393 /* (XtSelectionCallbackProc) */ PasteGameCB,
3394 NULL, /* client_data passed to PasteGameCB */
3396 /* better to use the time field from the event that triggered the
3397 * call to this function, but that isn't trivial to get
3406 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3413 { // bassic primitive for determining if modifier keys are pressed
3414 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3417 XQueryKeymap(xDisplay,keys);
3418 for(i=0; i<6; i++) {
3420 j = XKeysymToKeycode(xDisplay, codes[i]);
3421 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3427 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3431 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3432 if ( n == 1 && *buf >= 32 // printable
3433 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3434 ) BoxAutoPopUp (buf);
3438 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3439 { // [HGM] input: let up-arrow recall previous line from history
3444 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3445 { // [HGM] input: let down-arrow recall next line from history
3450 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3456 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3458 if (!TempBackwardActive) {
3459 TempBackwardActive = True;
3465 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3467 /* Check to see if triggered by a key release event for a repeating key.
3468 * If so the next queued event will be a key press of the same key at the same time */
3469 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3471 XPeekEvent(xDisplay, &next);
3472 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3473 next.xkey.keycode == event->xkey.keycode)
3477 TempBackwardActive = False;
3481 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3482 { // called as key binding
3485 if (nprms && *nprms > 0)
3489 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3495 { // called from menu
3496 ManInner(NULL, NULL, NULL, NULL);
3500 SetWindowTitle (char *text, char *title, char *icon)
3504 if (appData.titleInWindow) {
3506 XtSetArg(args[i], XtNlabel, text); i++;
3507 XtSetValues(titleWidget, args, i);
3510 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3511 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3512 XtSetValues(shellWidget, args, i);
3513 XSync(xDisplay, False);
3518 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3524 DisplayIcsInteractionTitle (String message)
3526 if (oldICSInteractionTitle == NULL) {
3527 /* Magic to find the old window title, adapted from vim */
3528 char *wina = getenv("WINDOWID");
3530 Window win = (Window) atoi(wina);
3531 Window root, parent, *children;
3532 unsigned int nchildren;
3533 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3535 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3536 if (!XQueryTree(xDisplay, win, &root, &parent,
3537 &children, &nchildren)) break;
3538 if (children) XFree((void *)children);
3539 if (parent == root || parent == 0) break;
3542 XSetErrorHandler(oldHandler);
3544 if (oldICSInteractionTitle == NULL) {
3545 oldICSInteractionTitle = "xterm";
3548 printf("\033]0;%s\007", message);
3553 XtIntervalId delayedEventTimerXID = 0;
3554 DelayedEventCallback delayedEventCallback = 0;
3559 delayedEventTimerXID = 0;
3560 delayedEventCallback();
3564 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3566 if(delayedEventTimerXID && delayedEventCallback == cb)
3567 // [HGM] alive: replace, rather than add or flush identical event
3568 XtRemoveTimeOut(delayedEventTimerXID);
3569 delayedEventCallback = cb;
3570 delayedEventTimerXID =
3571 XtAppAddTimeOut(appContext, millisec,
3572 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3575 DelayedEventCallback
3578 if (delayedEventTimerXID) {
3579 return delayedEventCallback;
3586 CancelDelayedEvent ()
3588 if (delayedEventTimerXID) {
3589 XtRemoveTimeOut(delayedEventTimerXID);
3590 delayedEventTimerXID = 0;
3594 XtIntervalId loadGameTimerXID = 0;
3597 LoadGameTimerRunning ()
3599 return loadGameTimerXID != 0;
3603 StopLoadGameTimer ()
3605 if (loadGameTimerXID != 0) {
3606 XtRemoveTimeOut(loadGameTimerXID);
3607 loadGameTimerXID = 0;
3615 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3617 loadGameTimerXID = 0;
3622 StartLoadGameTimer (long millisec)
3625 XtAppAddTimeOut(appContext, millisec,
3626 (XtTimerCallbackProc) LoadGameTimerCallback,
3630 XtIntervalId analysisClockXID = 0;
3633 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3635 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3636 || appData.icsEngineAnalyze) { // [DM]
3637 AnalysisPeriodicEvent(0);
3638 StartAnalysisClock();
3643 StartAnalysisClock ()
3646 XtAppAddTimeOut(appContext, 2000,
3647 (XtTimerCallbackProc) AnalysisClockCallback,
3651 XtIntervalId clockTimerXID = 0;
3654 ClockTimerRunning ()
3656 return clockTimerXID != 0;
3662 if (clockTimerXID != 0) {
3663 XtRemoveTimeOut(clockTimerXID);
3672 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3679 StartClockTimer (long millisec)
3682 XtAppAddTimeOut(appContext, millisec,
3683 (XtTimerCallbackProc) ClockTimerCallback,
3688 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3692 Widget w = (Widget) opt->handle;
3694 /* check for low time warning */
3695 Pixel foregroundOrWarningColor = timerForegroundPixel;
3698 appData.lowTimeWarning &&
3699 (timer / 1000) < appData.icsAlarmTime)
3700 foregroundOrWarningColor = lowTimeWarningColor;
3702 if (appData.clockMode) {
3703 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
3704 XtSetArg(args[0], XtNlabel, buf);
3706 snprintf(buf, MSG_SIZ, "%s ", color);
3707 XtSetArg(args[0], XtNlabel, buf);
3712 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3713 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3715 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3716 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3719 XtSetValues(w, args, 3);
3722 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3725 SetClockIcon (int color)
3728 Pixmap pm = *clockIcons[color];
3729 if (iconPixmap != pm) {
3731 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3732 XtSetValues(shellWidget, args, 1);
3737 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3739 InputSource *is = (InputSource *) closure;
3744 if (is->lineByLine) {
3745 count = read(is->fd, is->unused,
3746 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3748 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3751 is->unused += count;
3753 while (p < is->unused) {
3754 q = memchr(p, '\n', is->unused - p);
3755 if (q == NULL) break;
3757 (is->func)(is, is->closure, p, q - p, 0);
3761 while (p < is->unused) {
3766 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3771 (is->func)(is, is->closure, is->buf, count, error);
3776 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3779 ChildProc *cp = (ChildProc *) pr;
3781 is = (InputSource *) calloc(1, sizeof(InputSource));
3782 is->lineByLine = lineByLine;
3786 is->fd = fileno(stdin);
3788 is->kind = cp->kind;
3789 is->fd = cp->fdFrom;
3792 is->unused = is->buf;
3795 is->xid = XtAppAddInput(appContext, is->fd,
3796 (XtPointer) (XtInputReadMask),
3797 (XtInputCallbackProc) DoInputCallback,
3799 is->closure = closure;
3800 return (InputSourceRef) is;
3804 RemoveInputSource (InputSourceRef isr)
3806 InputSource *is = (InputSource *) isr;
3808 if (is->xid == 0) return;
3809 XtRemoveInput(is->xid);
3813 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3815 /* Masks for XPM pieces. Black and white pieces can have
3816 different shapes, but in the interest of retaining my
3817 sanity pieces must have the same outline on both light
3818 and dark squares, and all pieces must use the same
3819 background square colors/images. */
3821 static int xpmDone = 0;
3822 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3823 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3824 static cairo_surface_t *c_animBufs[3*NrOfAnims]; // newBuf, saveBuf
3827 CreateAnimMasks (int pieceDepth)
3833 unsigned long plane;
3836 /* Need a bitmap just to get a GC with right depth */
3837 buf = XCreatePixmap(xDisplay, xBoardWindow,
3839 values.foreground = 1;
3840 values.background = 0;
3841 /* Don't use XtGetGC, not read only */
3842 maskGC = XCreateGC(xDisplay, buf,
3843 GCForeground | GCBackground, &values);
3844 XFreePixmap(xDisplay, buf);
3846 buf = XCreatePixmap(xDisplay, xBoardWindow,
3847 squareSize, squareSize, pieceDepth);
3848 values.foreground = XBlackPixel(xDisplay, xScreen);
3849 values.background = XWhitePixel(xDisplay, xScreen);
3850 bufGC = XCreateGC(xDisplay, buf,
3851 GCForeground | GCBackground, &values);
3853 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3854 /* Begin with empty mask */
3855 if(!xpmDone) // [HGM] pieces: keep using existing
3856 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3857 squareSize, squareSize, 1);
3858 XSetFunction(xDisplay, maskGC, GXclear);
3859 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3860 0, 0, squareSize, squareSize);
3862 /* Take a copy of the piece */
3867 XSetFunction(xDisplay, bufGC, GXcopy);
3868 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3870 0, 0, squareSize, squareSize, 0, 0);
3872 /* XOR the background (light) over the piece */
3873 XSetFunction(xDisplay, bufGC, GXxor);
3875 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3876 0, 0, squareSize, squareSize, 0, 0);
3878 XSetForeground(xDisplay, bufGC, lightSquareColor);
3879 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3882 /* We now have an inverted piece image with the background
3883 erased. Construct mask by just selecting all the non-zero
3884 pixels - no need to reconstruct the original image. */
3885 XSetFunction(xDisplay, maskGC, GXor);
3887 /* Might be quicker to download an XImage and create bitmap
3888 data from it rather than this N copies per piece, but it
3889 only takes a fraction of a second and there is a much
3890 longer delay for loading the pieces. */
3891 for (n = 0; n < pieceDepth; n ++) {
3892 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3893 0, 0, squareSize, squareSize,
3899 XFreePixmap(xDisplay, buf);
3900 XFreeGC(xDisplay, bufGC);
3901 XFreeGC(xDisplay, maskGC);
3905 InitAnimState (AnimNr anr, XWindowAttributes *info)
3911 DrawSeekOpen(); // set cs to board widget
3912 if(c_animBufs[anr]) cairo_surface_destroy (c_animBufs[anr]);
3913 if(c_animBufs[anr+2]) cairo_surface_destroy (c_animBufs[anr+2]);
3914 c_animBufs[anr+4] = csBoardWindow;
3915 c_animBufs[anr+2] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3916 c_animBufs[anr] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3919 /* Each buffer is square size, same depth as window */
3920 animBufs[anr+4] = xBoardWindow;
3921 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3922 squareSize, squareSize, info->depth);
3923 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3924 squareSize, squareSize, info->depth);
3926 /* Create a plain GC for blitting */
3927 mask = GCForeground | GCBackground | GCFunction |
3928 GCPlaneMask | GCGraphicsExposures;
3929 values.foreground = XBlackPixel(xDisplay, xScreen);
3930 values.background = XWhitePixel(xDisplay, xScreen);
3931 values.function = GXcopy;
3932 values.plane_mask = AllPlanes;
3933 values.graphics_exposures = False;
3934 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3936 /* Piece will be copied from an existing context at
3937 the start of each new animation/drag. */
3938 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3940 /* Outline will be a read-only copy of an existing */
3941 animGCs[anr+4] = None;
3947 XWindowAttributes info;
3949 if (!cairoAnimate && xpmDone && gameInfo.variant == oldVariant) return;
3950 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3951 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3953 InitAnimState(Game, &info);
3954 InitAnimState(Player, &info);
3956 /* For XPM pieces, we need bitmaps to use as masks. */
3957 if (useImages & !xpmDone)
3958 CreateAnimMasks(info.depth), xpmDone = 1;
3963 static Boolean frameWaiting;
3966 FrameAlarm (int sig)
3968 frameWaiting = False;
3969 /* In case System-V style signals. Needed?? */
3970 signal(SIGALRM, FrameAlarm);
3974 FrameDelay (int time)
3976 struct itimerval delay;
3978 XSync(xDisplay, False);
3981 frameWaiting = True;
3982 signal(SIGALRM, FrameAlarm);
3983 delay.it_interval.tv_sec =
3984 delay.it_value.tv_sec = time / 1000;
3985 delay.it_interval.tv_usec =
3986 delay.it_value.tv_usec = (time % 1000) * 1000;
3987 setitimer(ITIMER_REAL, &delay, NULL);
3988 while (frameWaiting) pause();
3989 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3990 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3991 setitimer(ITIMER_REAL, &delay, NULL);
3998 FrameDelay (int time)
4000 XSync(xDisplay, False);
4002 usleep(time * 1000);
4008 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
4012 /* Bitmap for piece being moved. */
4013 if (appData.monoMode) {
4014 *mask = *pieceToSolid(piece);
4015 } else if (useImages) {
4017 *mask = xpmMask[piece];
4019 *mask = ximMaskPm[piece];
4022 *mask = *pieceToSolid(piece);
4025 /* GC for piece being moved. Square color doesn't matter, but
4026 since it gets modified we make a copy of the original. */
4028 if (appData.monoMode)
4033 if (appData.monoMode)
4038 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
4040 /* Outline only used in mono mode and is not modified */
4042 *outline = bwPieceGC;
4044 *outline = wbPieceGC;
4048 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
4053 /* Draw solid rectangle which will be clipped to shape of piece */
4054 XFillRectangle(xDisplay, dest, clip,
4055 0, 0, squareSize, squareSize);
4056 if (appData.monoMode)
4057 /* Also draw outline in contrasting color for black
4058 on black / white on white cases */
4059 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
4060 0, 0, squareSize, squareSize, 0, 0, 1);
4062 /* Copy the piece */
4067 if(appData.upsideDown && flipView) kind ^= 2;
4068 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4070 0, 0, squareSize, squareSize,
4076 CairoOverlayPiece (ChessSquare piece, cairo_surface_t *dest)
4078 static ChessSquare oldPiece = -1;
4080 static cairo_t *pieceSource;
4081 if(piece != oldPiece || squareSize != oldSize) { // try make it faster by only changing cr if we need other piece
4082 if(pieceSource) cairo_destroy (pieceSource);
4083 pieceSource = cairo_create (dest);
4084 cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
4085 oldPiece = piece; oldSize = squareSize;
4087 cairo_paint(pieceSource);
4091 InsertPiece (AnimNr anr, ChessSquare piece)
4094 CairoOverlayPiece(piece, c_animBufs[anr]);
4096 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
4100 DrawBlank (AnimNr anr, int x, int y, int startColor)
4103 BlankSquare(x, y, startColor, EmptySquare, (Drawable) c_animBufs[anr+2], 0);
4105 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
4108 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
4109 int srcX, int srcY, int width, int height, int destX, int destY)
4112 cairo_t *cr;// = cairo_create (c_animBufs[anr+destBuf]);
4113 cr = cairo_create (c_animBufs[anr+destBuf]);
4114 if(c_animBufs[anr+srcBuf] == csBoardWindow)
4115 cairo_set_source_surface (cr, csBoardBackup, destX - srcX, destY - srcY);
4117 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
4118 cairo_rectangle (cr, destX, destY, width, height);
4121 if(c_animBufs[anr+destBuf] == csBoardWindow) {
4122 cr = cairo_create (csBoardBackup); // also draw to backup
4123 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
4124 cairo_rectangle (cr, destX, destY, width, height);
4129 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
4130 srcX, srcY, width, height, destX, destY);
4134 SetDragPiece (AnimNr anr, ChessSquare piece)
4137 if(cairoAnimate) return;
4138 /* The piece will be drawn using its own bitmap as a matte */
4139 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
4140 XSetClipMask(xDisplay, animGCs[anr+2], mask);
4143 /* [AS] Arrow highlighting support */
4145 void DrawPolygon(Pnt arrow[], int nr)
4146 { // for now on own surface; eventually this should become a global that is only destroyed on resize
4147 cairo_surface_t *boardSurface;
4150 int w = lineGap + BOARD_WIDTH * (squareSize + lineGap);
4151 int h = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
4152 boardSurface = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), w, h);
4153 cr = cairo_create (boardSurface);
4154 cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
4155 for (i=0;i<nr;i++) {
4156 cairo_line_to(cr, arrow[i].x, arrow[i].y);
4158 if(appData.monoMode) { // should we always outline arrow?
4159 cairo_line_to(cr, arrow[0].x, arrow[0].y);
4160 SetPen(cr, 2, "#000000", 0);
4161 cairo_stroke_preserve(cr);
4163 SetPen(cr, 2, appData.highlightSquareColor, 0);
4168 cairo_surface_destroy (boardSurface);
4172 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
4174 char buf[MSG_SIZ], *logoName = buf;
4175 if(appData.logo[n][0]) {
4176 logoName = appData.logo[n];
4177 } else if(appData.autoLogo) {
4178 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
4179 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
4180 } else if(appData.directory[n] && appData.directory[n][0]) {
4181 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
4185 { ASSIGN(cps->programLogo, logoName); }
4189 UpdateLogos (int displ)
4191 if(optList[W_WHITE-1].handle == NULL) return;
4192 LoadLogo(&first, 0, 0);
4193 LoadLogo(&second, 1, appData.icsActive);
4194 if(displ) DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);