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 //--------------------------------------------------------------------------------------------
930 #define BoardSize int
932 InitDrawingSizes (BoardSize boardSize, int flags)
933 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
934 Dimension boardWidth, boardHeight, w, h;
936 static Dimension oldWidth, oldHeight;
937 static VariantClass oldVariant;
938 static int oldMono = -1, oldTwoBoards = 0;
940 if(!formWidget) return;
942 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
943 oldTwoBoards = twoBoards;
945 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
946 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
947 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
949 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
951 oldWidth = boardWidth; oldHeight = boardHeight;
955 * Inhibit shell resizing.
957 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW ;
958 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
959 shellArgs[4].value = shellArgs[2].value = w;
960 shellArgs[5].value = shellArgs[3].value = h;
961 XtSetValues(shellWidget, &shellArgs[0], 6);
963 XSync(xDisplay, False);
967 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
970 if(gameInfo.variant != oldVariant) { // and only if variant changed
975 for(p=0; p<=(int)WhiteKing; p++)
976 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
977 if(gameInfo.variant == VariantShogi) {
978 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
979 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
980 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
981 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
982 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
985 if(gameInfo.variant == VariantGothic) {
986 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
989 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
990 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
991 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
994 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
995 for(p=0; p<=(int)WhiteKing; p++)
996 ximMaskPm[p] = ximMaskPm2[p]; // defaults
997 if(gameInfo.variant == VariantShogi) {
998 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
999 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1000 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1001 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1002 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1005 if(gameInfo.variant == VariantGothic) {
1006 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1009 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1010 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1011 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1016 for(i=0; i<2; i++) {
1018 for(p=0; p<=(int)WhiteKing; p++)
1019 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1020 if(gameInfo.variant == VariantShogi) {
1021 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1022 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1023 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1024 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1025 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1028 if(gameInfo.variant == VariantGothic) {
1029 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1032 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1033 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1034 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1038 for(i=0; i<2; i++) {
1040 printf("Copy pieces\n");
1041 for(p=0; p<=(int)WhiteKing; p++)
1042 pngPieceBitmaps[i][p] = pngPieceBitmaps2[i][p]; // defaults
1044 oldMono = -10; // kludge to force recreation of animation masks
1045 oldVariant = gameInfo.variant;
1048 if(appData.monoMode != oldMono)
1051 oldMono = appData.monoMode;
1055 MakeOneColor (char *name, Pixel *color)
1057 XrmValue vFrom, vTo;
1058 if (!appData.monoMode) {
1059 vFrom.addr = (caddr_t) name;
1060 vFrom.size = strlen(name);
1061 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1062 if (vTo.addr == NULL) {
1063 appData.monoMode = True;
1066 *color = *(Pixel *) vTo.addr;
1074 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1075 int forceMono = False;
1077 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1078 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1079 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1080 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1081 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1082 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1083 if (appData.lowTimeWarning)
1084 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1085 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1086 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1093 { // [HGM] taken out of main
1095 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1096 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1097 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1099 if (appData.bitmapDirectory[0] != NULLCHAR) {
1103 CreateXPMBoard(appData.liteBackTextureFile, 1);
1104 CreateXPMBoard(appData.darkBackTextureFile, 0);
1106 if (appData.pngDirectory[0] != NULLCHAR) { // for now do in parallel
1111 /* Create regular pieces */
1112 if (!useImages) CreatePieces();
1117 InitDrawingParams ()
1119 MakeColors(); CreateGCs(True);
1124 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1125 { // detervtomine what fonts to use, and create them
1129 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1130 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1131 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1132 appData.font = fontTable[MESSAGE_FONT][squareSize];
1133 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1134 appData.coordFont = fontTable[COORD_FONT][squareSize];
1137 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1138 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1139 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1140 fontSet = CreateFontSet(appData.font);
1141 clockFontSet = CreateFontSet(appData.clockFont);
1143 /* For the coordFont, use the 0th font of the fontset. */
1144 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1145 XFontStruct **font_struct_list;
1146 XFontSetExtents *fontSize;
1147 char **font_name_list;
1148 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1149 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1150 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1151 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1152 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1155 appData.font = FindFont(appData.font, fontPxlSize);
1156 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1157 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1158 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1159 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1160 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1161 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1162 // textHeight in !NLS mode!
1164 countFontID = coordFontID; // [HGM] holdings
1165 countFontStruct = coordFontStruct;
1167 xdb = XtDatabase(xDisplay);
1169 XrmPutLineResource(&xdb, "*international: True");
1170 vTo.size = sizeof(XFontSet);
1171 vTo.addr = (XtPointer) &fontSet;
1172 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1174 XrmPutStringResource(&xdb, "*font", appData.font);
1179 PrintArg (ArgType t)
1184 case ArgInt: p = " N"; break;
1185 case ArgString: p = " STR"; break;
1186 case ArgBoolean: p = " TF"; break;
1187 case ArgSettingsFilename:
1188 case ArgFilename: p = " FILE"; break;
1189 case ArgX: p = " Nx"; break;
1190 case ArgY: p = " Ny"; break;
1191 case ArgAttribs: p = " TEXTCOL"; break;
1192 case ArgColor: p = " COL"; break;
1193 case ArgFont: p = " FONT"; break;
1194 case ArgBoardSize: p = " SIZE"; break;
1195 case ArgFloat: p = " FLOAT"; break;
1200 case ArgCommSettings:
1211 ArgDescriptor *q, *p = argDescriptors+5;
1212 printf("\nXBoard accepts the following options:\n"
1213 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1214 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1215 " SIZE = board-size spec(s)\n"
1216 " Within parentheses are short forms, or options to set to true or false.\n"
1217 " Persistent options (saved in the settings file) are marked with *)\n\n");
1219 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1220 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1221 if(p->save) strcat(buf+len, "*");
1222 for(q=p+1; q->argLoc == p->argLoc; q++) {
1223 if(q->argName[0] == '-') continue;
1224 strcat(buf+len, q == p+1 ? " (" : " ");
1225 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1227 if(q != p+1) strcat(buf+len, ")");
1229 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1232 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1236 main (int argc, char **argv)
1238 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1239 XSetWindowAttributes window_attributes;
1241 Dimension boardWidth, boardHeight, w, h;
1243 int forceMono = False;
1245 srandom(time(0)); // [HGM] book: make random truly random
1247 setbuf(stdout, NULL);
1248 setbuf(stderr, NULL);
1251 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1252 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1256 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1261 programName = strrchr(argv[0], '/');
1262 if (programName == NULL)
1263 programName = argv[0];
1268 XtSetLanguageProc(NULL, NULL, NULL);
1269 if (appData.debugMode) {
1270 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1273 bindtextdomain(PACKAGE, LOCALEDIR);
1274 textdomain(PACKAGE);
1277 appData.boardSize = "";
1278 InitAppData(ConvertToLine(argc, argv));
1280 if (p == NULL) p = "/tmp";
1281 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1282 gameCopyFilename = (char*) malloc(i);
1283 gamePasteFilename = (char*) malloc(i);
1284 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1285 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1287 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1288 static char buf[MSG_SIZ];
1289 EscapeExpand(buf, appData.firstInitString);
1290 appData.firstInitString = strdup(buf);
1291 EscapeExpand(buf, appData.secondInitString);
1292 appData.secondInitString = strdup(buf);
1293 EscapeExpand(buf, appData.firstComputerString);
1294 appData.firstComputerString = strdup(buf);
1295 EscapeExpand(buf, appData.secondComputerString);
1296 appData.secondComputerString = strdup(buf);
1299 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1302 if (chdir(chessDir) != 0) {
1303 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1309 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1310 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1311 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1312 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1315 setbuf(debugFP, NULL);
1318 /* [HGM,HR] make sure board size is acceptable */
1319 if(appData.NrFiles > BOARD_FILES ||
1320 appData.NrRanks > BOARD_RANKS )
1321 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1324 /* This feature does not work; animation needs a rewrite */
1325 appData.highlightDragging = FALSE;
1329 gameInfo.variant = StringToVariant(appData.variant);
1330 InitPosition(FALSE);
1333 XtAppInitialize(&appContext, "XBoard", shellOptions,
1334 XtNumber(shellOptions),
1335 &argc, argv, xboardResources, NULL, 0);
1337 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1338 clientResources, XtNumber(clientResources),
1341 xDisplay = XtDisplay(shellWidget);
1342 xScreen = DefaultScreen(xDisplay);
1343 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1346 * determine size, based on supplied or remembered -size, or screen size
1348 if (isdigit(appData.boardSize[0])) {
1349 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1350 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1351 &fontPxlSize, &smallLayout, &tinyLayout);
1353 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1354 programName, appData.boardSize);
1358 /* Find some defaults; use the nearest known size */
1359 SizeDefaults *szd, *nearest;
1360 int distance = 99999;
1361 nearest = szd = sizeDefaults;
1362 while (szd->name != NULL) {
1363 if (abs(szd->squareSize - squareSize) < distance) {
1365 distance = abs(szd->squareSize - squareSize);
1366 if (distance == 0) break;
1370 if (i < 2) lineGap = nearest->lineGap;
1371 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1372 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1373 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1374 if (i < 6) smallLayout = nearest->smallLayout;
1375 if (i < 7) tinyLayout = nearest->tinyLayout;
1378 SizeDefaults *szd = sizeDefaults;
1379 if (*appData.boardSize == NULLCHAR) {
1380 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1381 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1384 if (szd->name == NULL) szd--;
1385 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1387 while (szd->name != NULL &&
1388 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1389 if (szd->name == NULL) {
1390 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1391 programName, appData.boardSize);
1395 squareSize = szd->squareSize;
1396 lineGap = szd->lineGap;
1397 clockFontPxlSize = szd->clockFontPxlSize;
1398 coordFontPxlSize = szd->coordFontPxlSize;
1399 fontPxlSize = szd->fontPxlSize;
1400 smallLayout = szd->smallLayout;
1401 tinyLayout = szd->tinyLayout;
1402 // [HGM] font: use defaults from settings file if available and not overruled
1405 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1406 if (strlen(appData.pixmapDirectory) > 0) {
1407 p = ExpandPathName(appData.pixmapDirectory);
1409 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1410 appData.pixmapDirectory);
1413 if (appData.debugMode) {
1414 fprintf(stderr, _("\
1415 XBoard square size (hint): %d\n\
1416 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1418 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1419 if (appData.debugMode) {
1420 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1423 defaultLineGap = lineGap;
1424 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1426 /* [HR] height treated separately (hacked) */
1427 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1428 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1431 * Determine what fonts to use.
1433 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1436 * Detect if there are not enough colors available and adapt.
1438 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1439 appData.monoMode = True;
1442 forceMono = MakeColors();
1445 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1447 appData.monoMode = True;
1450 if (appData.monoMode && appData.debugMode) {
1451 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1452 (unsigned long) XWhitePixel(xDisplay, xScreen),
1453 (unsigned long) XBlackPixel(xDisplay, xScreen));
1456 ParseIcsTextColors();
1458 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1464 layoutName = "tinyLayout";
1465 } else if (smallLayout) {
1466 layoutName = "smallLayout";
1468 layoutName = "normalLayout";
1471 optList = BoardPopUp(squareSize, lineGap, (void*)
1477 boardWidget = optList[W_BOARD].handle;
1478 menuBarWidget = optList[W_MENU].handle;
1479 dropMenu = optList[W_DROP].handle;
1480 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1481 formWidget = XtParent(boardWidget);
1482 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1483 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1484 XtGetValues(optList[W_WHITE].handle, args, 2);
1485 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1486 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1487 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1488 XtGetValues(optList[W_PAUSE].handle, args, 2);
1490 AppendEnginesToMenu(appData.recentEngineList);
1492 xBoardWindow = XtWindow(boardWidget);
1494 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1495 // not need to go into InitDrawingSizes().
1498 * Create X checkmark bitmap and initialize option menu checks.
1500 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1501 checkmark_bits, checkmark_width, checkmark_height);
1507 ReadBitmap(&wIconPixmap, "icon_white.bm",
1508 icon_white_bits, icon_white_width, icon_white_height);
1509 ReadBitmap(&bIconPixmap, "icon_black.bm",
1510 icon_black_bits, icon_black_width, icon_black_height);
1511 iconPixmap = wIconPixmap;
1513 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1514 XtSetValues(shellWidget, args, i);
1517 * Create a cursor for the board widget.
1519 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1520 XChangeWindowAttributes(xDisplay, xBoardWindow,
1521 CWCursor, &window_attributes);
1524 * Inhibit shell resizing.
1526 shellArgs[0].value = (XtArgVal) &w;
1527 shellArgs[1].value = (XtArgVal) &h;
1528 XtGetValues(shellWidget, shellArgs, 2);
1529 shellArgs[4].value = shellArgs[2].value = w;
1530 shellArgs[5].value = shellArgs[3].value = h;
1531 XtSetValues(shellWidget, &shellArgs[2], 4);
1532 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1533 marginH = h - boardHeight;
1535 CatchDeleteWindow(shellWidget, "QuitProc");
1541 if(appData.logoSize)
1542 { // locate and read user logo
1544 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1545 ASSIGN(userLogo, buf);
1548 if (appData.animate || appData.animateDragging)
1551 XtAugmentTranslations(formWidget,
1552 XtParseTranslationTable(globalTranslations));
1554 XtAddEventHandler(formWidget, KeyPressMask, False,
1555 (XtEventHandler) MoveTypeInProc, NULL);
1556 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1557 (XtEventHandler) EventProc, NULL);
1559 /* [AS] Restore layout */
1560 if( wpMoveHistory.visible ) {
1564 if( wpEvalGraph.visible )
1569 if( wpEngineOutput.visible ) {
1570 EngineOutputPopUp();
1575 if (errorExitStatus == -1) {
1576 if (appData.icsActive) {
1577 /* We now wait until we see "login:" from the ICS before
1578 sending the logon script (problems with timestamp otherwise) */
1579 /*ICSInitScript();*/
1580 if (appData.icsInputBox) ICSInputBoxPopUp();
1584 signal(SIGWINCH, TermSizeSigHandler);
1586 signal(SIGINT, IntSigHandler);
1587 signal(SIGTERM, IntSigHandler);
1588 if (*appData.cmailGameName != NULLCHAR) {
1589 signal(SIGUSR1, CmailSigHandler);
1593 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1596 // XtSetKeyboardFocus(shellWidget, formWidget);
1597 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1599 XtAppMainLoop(appContext);
1600 if (appData.debugMode) fclose(debugFP); // [DM] debug
1605 TermSizeSigHandler (int sig)
1611 IntSigHandler (int sig)
1617 CmailSigHandler (int sig)
1622 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1624 /* Activate call-back function CmailSigHandlerCallBack() */
1625 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1627 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1631 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1634 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1636 /**** end signal code ****/
1639 #define Abs(n) ((n)<0 ? -(n) : (n))
1643 InsertPxlSize (char *pattern, int targetPxlSize)
1645 char *base_fnt_lst, strInt[12], *p, *q;
1646 int alternatives, i, len, strIntLen;
1649 * Replace the "*" (if present) in the pixel-size slot of each
1650 * alternative with the targetPxlSize.
1654 while ((p = strchr(p, ',')) != NULL) {
1658 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1659 strIntLen = strlen(strInt);
1660 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1664 while (alternatives--) {
1665 char *comma = strchr(p, ',');
1666 for (i=0; i<14; i++) {
1667 char *hyphen = strchr(p, '-');
1669 if (comma && hyphen > comma) break;
1670 len = hyphen + 1 - p;
1671 if (i == 7 && *p == '*' && len == 2) {
1673 memcpy(q, strInt, strIntLen);
1683 len = comma + 1 - p;
1690 return base_fnt_lst;
1694 CreateFontSet (char *base_fnt_lst)
1697 char **missing_list;
1701 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1702 &missing_list, &missing_count, &def_string);
1703 if (appData.debugMode) {
1705 XFontStruct **font_struct_list;
1706 char **font_name_list;
1707 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1709 fprintf(debugFP, " got list %s, locale %s\n",
1710 XBaseFontNameListOfFontSet(fntSet),
1711 XLocaleOfFontSet(fntSet));
1712 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1713 for (i = 0; i < count; i++) {
1714 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1717 for (i = 0; i < missing_count; i++) {
1718 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1721 if (fntSet == NULL) {
1722 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1727 #else // not ENABLE_NLS
1729 * Find a font that matches "pattern" that is as close as
1730 * possible to the targetPxlSize. Prefer fonts that are k
1731 * pixels smaller to fonts that are k pixels larger. The
1732 * pattern must be in the X Consortium standard format,
1733 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1734 * The return value should be freed with XtFree when no
1738 FindFont (char *pattern, int targetPxlSize)
1740 char **fonts, *p, *best, *scalable, *scalableTail;
1741 int i, j, nfonts, minerr, err, pxlSize;
1743 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1745 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1746 programName, pattern);
1753 for (i=0; i<nfonts; i++) {
1756 if (*p != '-') continue;
1758 if (*p == NULLCHAR) break;
1759 if (*p++ == '-') j++;
1761 if (j < 7) continue;
1764 scalable = fonts[i];
1767 err = pxlSize - targetPxlSize;
1768 if (Abs(err) < Abs(minerr) ||
1769 (minerr > 0 && err < 0 && -err == minerr)) {
1775 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1776 /* If the error is too big and there is a scalable font,
1777 use the scalable font. */
1778 int headlen = scalableTail - scalable;
1779 p = (char *) XtMalloc(strlen(scalable) + 10);
1780 while (isdigit(*scalableTail)) scalableTail++;
1781 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1783 p = (char *) XtMalloc(strlen(best) + 2);
1784 safeStrCpy(p, best, strlen(best)+1 );
1786 if (appData.debugMode) {
1787 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1788 pattern, targetPxlSize, p);
1790 XFreeFontNames(fonts);
1797 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1798 // must be called before all non-first callse to CreateGCs()
1799 XtReleaseGC(shellWidget, highlineGC);
1800 XtReleaseGC(shellWidget, lightSquareGC);
1801 XtReleaseGC(shellWidget, darkSquareGC);
1802 XtReleaseGC(shellWidget, lineGC);
1803 if (appData.monoMode) {
1804 if (DefaultDepth(xDisplay, xScreen) == 1) {
1805 XtReleaseGC(shellWidget, wbPieceGC);
1807 XtReleaseGC(shellWidget, bwPieceGC);
1810 XtReleaseGC(shellWidget, prelineGC);
1811 XtReleaseGC(shellWidget, wdPieceGC);
1812 XtReleaseGC(shellWidget, wlPieceGC);
1813 XtReleaseGC(shellWidget, bdPieceGC);
1814 XtReleaseGC(shellWidget, blPieceGC);
1819 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1821 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1822 | GCBackground | GCFunction | GCPlaneMask;
1823 gc_values->foreground = foreground;
1824 gc_values->background = background;
1825 return XtGetGC(shellWidget, value_mask, gc_values);
1829 CreateGCs (int redo)
1831 XGCValues gc_values;
1833 Pixel white = XWhitePixel(xDisplay, xScreen);
1834 Pixel black = XBlackPixel(xDisplay, xScreen);
1836 gc_values.plane_mask = AllPlanes;
1837 gc_values.line_width = lineGap;
1838 gc_values.line_style = LineSolid;
1839 gc_values.function = GXcopy;
1842 DeleteGCs(); // called a second time; clean up old GCs first
1843 } else { // [HGM] grid and font GCs created on first call only
1844 coordGC = CreateOneGC(&gc_values, black, white);
1845 XSetFont(xDisplay, coordGC, coordFontID);
1847 // [HGM] make font for holdings counts (white on black)
1848 countGC = CreateOneGC(&gc_values, white, black);
1849 XSetFont(xDisplay, countGC, countFontID);
1851 lineGC = CreateOneGC(&gc_values, black, black);
1853 if (appData.monoMode) {
1855 highlineGC = CreateOneGC(&gc_values, white, white);
1856 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1857 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1859 if (DefaultDepth(xDisplay, xScreen) == 1) {
1860 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1861 gc_values.function = GXcopyInverted;
1862 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1863 gc_values.function = GXcopy;
1864 if (XBlackPixel(xDisplay, xScreen) == 1) {
1865 bwPieceGC = darkSquareGC;
1866 wbPieceGC = copyInvertedGC;
1868 bwPieceGC = copyInvertedGC;
1869 wbPieceGC = lightSquareGC;
1874 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1875 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1876 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1877 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1878 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1879 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1880 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1881 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1886 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1894 fp = fopen(filename, "rb");
1896 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1903 for (y=0; y<h; ++y) {
1904 for (x=0; x<h; ++x) {
1909 XPutPixel(xim, x, y, blackPieceColor);
1911 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1914 XPutPixel(xim, x, y, darkSquareColor);
1916 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1919 XPutPixel(xim, x, y, whitePieceColor);
1921 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1924 XPutPixel(xim, x, y, lightSquareColor);
1926 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1934 /* create Pixmap of piece */
1935 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1937 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1940 /* create Pixmap of clipmask
1941 Note: We assume the white/black pieces have the same
1942 outline, so we make only 6 masks. This is okay
1943 since the XPM clipmask routines do the same. */
1945 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1947 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1950 /* now create the 1-bit version */
1951 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1954 values.foreground = 1;
1955 values.background = 0;
1957 /* Don't use XtGetGC, not read only */
1958 maskGC = XCreateGC(xDisplay, *mask,
1959 GCForeground | GCBackground, &values);
1960 XCopyPlane(xDisplay, temp, *mask, maskGC,
1961 0, 0, squareSize, squareSize, 0, 0, 1);
1962 XFreePixmap(xDisplay, temp);
1967 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1975 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1980 /* The XSynchronize calls were copied from CreatePieces.
1981 Not sure if needed, but can't hurt */
1982 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1985 /* temp needed by loadXIM() */
1986 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1987 0, 0, ss, ss, AllPlanes, XYPixmap);
1989 if (strlen(appData.pixmapDirectory) == 0) {
1993 if (appData.monoMode) {
1994 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1998 fprintf(stderr, _("\nLoading XIMs...\n"));
2000 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2001 fprintf(stderr, "%d", piece+1);
2002 for (kind=0; kind<4; kind++) {
2003 fprintf(stderr, ".");
2004 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2005 ExpandPathName(appData.pixmapDirectory),
2006 piece <= (int) WhiteKing ? "" : "w",
2007 pieceBitmapNames[piece],
2009 ximPieceBitmap[kind][piece] =
2010 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2011 0, 0, ss, ss, AllPlanes, XYPixmap);
2012 if (appData.debugMode)
2013 fprintf(stderr, _("(File:%s:) "), buf);
2014 loadXIM(ximPieceBitmap[kind][piece],
2016 &(xpmPieceBitmap2[kind][piece]),
2017 &(ximMaskPm2[piece]));
2018 if(piece <= (int)WhiteKing)
2019 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2021 fprintf(stderr," ");
2023 /* Load light and dark squares */
2024 /* If the LSQ and DSQ pieces don't exist, we will
2025 draw them with solid squares. */
2026 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2027 if (access(buf, 0) != 0) {
2031 fprintf(stderr, _("light square "));
2033 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2034 0, 0, ss, ss, AllPlanes, XYPixmap);
2035 if (appData.debugMode)
2036 fprintf(stderr, _("(File:%s:) "), buf);
2038 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2039 fprintf(stderr, _("dark square "));
2040 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2041 ExpandPathName(appData.pixmapDirectory), ss);
2042 if (appData.debugMode)
2043 fprintf(stderr, _("(File:%s:) "), buf);
2045 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2046 0, 0, ss, ss, AllPlanes, XYPixmap);
2047 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2048 xpmJailSquare = xpmLightSquare;
2050 fprintf(stderr, _("Done.\n"));
2052 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2055 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2059 CreateXPMBoard (char *s, int kind)
2063 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2064 if(strstr(s, ".png")) {
2065 cairo_surface_t *img = cairo_image_surface_create_from_png (s);
2067 useTexture |= kind + 1; pngBoardBitmap[kind] = img;
2068 textureW[kind] = cairo_image_surface_get_width (img);
2069 textureH[kind] = cairo_image_surface_get_height (img);
2072 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2073 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2079 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2080 // thisroutine has to be called t free the old piece pixmaps
2082 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2083 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2085 XFreePixmap(xDisplay, xpmLightSquare);
2086 XFreePixmap(xDisplay, xpmDarkSquare);
2095 u_int ss = squareSize;
2097 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2098 XpmColorSymbol symbols[4];
2099 static int redo = False;
2101 if(redo) FreeXPMPieces(); else redo = 1;
2103 /* The XSynchronize calls were copied from CreatePieces.
2104 Not sure if needed, but can't hurt */
2105 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2107 /* Setup translations so piece colors match square colors */
2108 symbols[0].name = "light_piece";
2109 symbols[0].value = appData.whitePieceColor;
2110 symbols[1].name = "dark_piece";
2111 symbols[1].value = appData.blackPieceColor;
2112 symbols[2].name = "light_square";
2113 symbols[2].value = appData.lightSquareColor;
2114 symbols[3].name = "dark_square";
2115 symbols[3].value = appData.darkSquareColor;
2117 attr.valuemask = XpmColorSymbols;
2118 attr.colorsymbols = symbols;
2119 attr.numsymbols = 4;
2121 if (appData.monoMode) {
2122 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2126 if (strlen(appData.pixmapDirectory) == 0) {
2127 XpmPieces* pieces = builtInXpms;
2130 while (pieces->size != squareSize && pieces->size) pieces++;
2131 if (!pieces->size) {
2132 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2135 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2136 for (kind=0; kind<4; kind++) {
2138 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2139 pieces->xpm[piece][kind],
2140 &(xpmPieceBitmap2[kind][piece]),
2141 NULL, &attr)) != 0) {
2142 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2146 if(piece <= (int) WhiteKing)
2147 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2151 xpmJailSquare = xpmLightSquare;
2155 fprintf(stderr, _("\nLoading XPMs...\n"));
2158 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2159 fprintf(stderr, "%d ", piece+1);
2160 for (kind=0; kind<4; kind++) {
2161 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2162 ExpandPathName(appData.pixmapDirectory),
2163 piece > (int) WhiteKing ? "w" : "",
2164 pieceBitmapNames[piece],
2166 if (appData.debugMode) {
2167 fprintf(stderr, _("(File:%s:) "), buf);
2169 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2170 &(xpmPieceBitmap2[kind][piece]),
2171 NULL, &attr)) != 0) {
2172 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2173 // [HGM] missing: read of unorthodox piece failed; substitute King.
2174 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2175 ExpandPathName(appData.pixmapDirectory),
2177 if (appData.debugMode) {
2178 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2180 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2181 &(xpmPieceBitmap2[kind][piece]),
2185 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2190 if(piece <= (int) WhiteKing)
2191 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2194 /* Load light and dark squares */
2195 /* If the LSQ and DSQ pieces don't exist, we will
2196 draw them with solid squares. */
2197 fprintf(stderr, _("light square "));
2198 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2199 if (access(buf, 0) != 0) {
2203 if (appData.debugMode)
2204 fprintf(stderr, _("(File:%s:) "), buf);
2206 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2207 &xpmLightSquare, NULL, &attr)) != 0) {
2208 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2211 fprintf(stderr, _("dark square "));
2212 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2213 ExpandPathName(appData.pixmapDirectory), ss);
2214 if (appData.debugMode) {
2215 fprintf(stderr, _("(File:%s:) "), buf);
2217 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2218 &xpmDarkSquare, NULL, &attr)) != 0) {
2219 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2223 xpmJailSquare = xpmLightSquare;
2224 fprintf(stderr, _("Done.\n"));
2226 oldVariant = -1; // kludge to force re-makig of animation masks
2227 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2230 #endif /* HAVE_LIBXPM */
2232 char *pngPieceNames[] = // must be in same order as internal piece encoding
2233 { "Pawn", "Knight", "Bishop", "Rook", "Queen", "Advisor", "Elephant", "Archbishop", "Marshall", "Gold", "Commoner",
2234 "Canon", "Nightrider", "CrownedBishop", "CrownedRook", "Princess", "Chancellor", "Hawk", "Lance", "Cobra", "Unicorn", "King",
2235 "GoldKnight", "GoldLance", "GoldPawn", "GoldSilver", NULL
2239 ScaleOnePiece (char *name, int color, int piece)
2243 cairo_surface_t *img, *cs;
2245 static cairo_surface_t *pngPieceImages[2][(int)BlackPawn+4]; // png 256 x 256 images
2247 if(pngPieceImages[color][piece] == NULL) { // if PNG file for this piece was not yet read, read it now and store it
2248 snprintf(buf, MSG_SIZ, "%s/%s%s.png", appData.pngDirectory, color ? "Black" : "White", pngPieceNames[piece]);
2249 pngPieceImages[color][piece] = img = cairo_image_surface_create_from_png (buf);
2250 w = cairo_image_surface_get_width (img);
2251 h = cairo_image_surface_get_height (img);
2252 if(w != 64 || h != 64) { printf("Bad png size %dx%d in %s\n", w, h, buf); exit(1); }
2255 // create new bitmap to hold scaled piece image (and remove any old)
2256 if(pngPieceBitmaps2[color][piece]) cairo_surface_destroy (pngPieceBitmaps2[color][piece]);
2257 pngPieceBitmaps2[color][piece] = cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
2258 if(piece <= WhiteKing) pngPieceBitmaps[color][piece] = cs;
2259 // scaled copying of the raw png image
2260 cr = cairo_create(cs);
2261 cairo_scale(cr, squareSize/64., squareSize/64.);
2262 cairo_set_source_surface (cr, img, 0, 0);
2265 cairo_surface_destroy (img);
2273 for(p=0; pngPieceNames[p]; p++) {
2274 ScaleOnePiece(pngPieceNames[p], 0, p);
2275 ScaleOnePiece(pngPieceNames[p], 1, p);
2280 /* No built-in bitmaps */
2285 u_int ss = squareSize;
2287 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2290 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2291 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2292 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2293 pieceBitmapNames[piece],
2294 ss, kind == SOLID ? 's' : 'o');
2295 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2296 if(piece <= (int)WhiteKing)
2297 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2301 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2305 /* With built-in bitmaps */
2309 BuiltInBits* bib = builtInBits;
2312 u_int ss = squareSize;
2314 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2317 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2319 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2320 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2321 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2322 pieceBitmapNames[piece],
2323 ss, kind == SOLID ? 's' : 'o');
2324 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2325 bib->bits[kind][piece], ss, ss);
2326 if(piece <= (int)WhiteKing)
2327 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2331 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2337 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2342 char msg[MSG_SIZ], fullname[MSG_SIZ];
2344 if (*appData.bitmapDirectory != NULLCHAR) {
2345 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2346 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2347 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2348 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2349 &w, &h, pm, &x_hot, &y_hot);
2350 fprintf(stderr, "load %s\n", name);
2351 if (errcode != BitmapSuccess) {
2353 case BitmapOpenFailed:
2354 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2356 case BitmapFileInvalid:
2357 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2359 case BitmapNoMemory:
2360 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2364 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2368 fprintf(stderr, _("%s: %s...using built-in\n"),
2370 } else if (w != wreq || h != hreq) {
2372 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2373 programName, fullname, w, h, wreq, hreq);
2379 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2389 if (lineGap == 0) return;
2391 /* [HR] Split this into 2 loops for non-square boards. */
2393 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2394 gridSegments[i].x1 = 0;
2395 gridSegments[i].x2 =
2396 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2397 gridSegments[i].y1 = gridSegments[i].y2
2398 = lineGap / 2 + (i * (squareSize + lineGap));
2401 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2402 gridSegments[j + i].y1 = 0;
2403 gridSegments[j + i].y2 =
2404 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2405 gridSegments[j + i].x1 = gridSegments[j + i].x2
2406 = lineGap / 2 + (j * (squareSize + lineGap));
2411 MarkMenuItem (char *menuRef, int state)
2413 MenuItem *item = MenuNameToItem(menuRef);
2417 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2418 XtSetValues(item->handle, args, 1);
2423 EnableNamedMenuItem (char *menuRef, int state)
2425 MenuItem *item = MenuNameToItem(menuRef);
2427 if(item) XtSetSensitive(item->handle, state);
2431 EnableButtonBar (int state)
2433 XtSetSensitive(optList[W_BUTTON].handle, state);
2438 SetMenuEnables (Enables *enab)
2440 while (enab->name != NULL) {
2441 EnableNamedMenuItem(enab->name, enab->value);
2447 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2448 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2450 if(*nprms == 0) return;
2451 item = MenuNameToItem(prms[0]);
2452 if(item) ((MenuProc *) item->proc) ();
2456 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2458 RecentEngineEvent((int) (intptr_t) addr);
2462 AppendMenuItem (char *msg, int n)
2464 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2476 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2477 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2478 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2479 dmEnables[i].piece);
2480 XtSetSensitive(entry, p != NULL || !appData.testLegality
2481 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2482 && !appData.icsActive));
2484 while (p && *p++ == dmEnables[i].piece) count++;
2485 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2487 XtSetArg(args[j], XtNlabel, label); j++;
2488 XtSetValues(entry, args, j);
2494 do_flash_delay (unsigned long msec)
2500 DoDrawBorder (cairo_surface_t *cs, int x, int y, int type)
2505 cr = cairo_create(cs);
2506 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
2507 cairo_rectangle(cr, x, y, squareSize+lineGap, squareSize+lineGap);
2508 SetPen(cr, lineGap, type == 1 ? appData.highlightSquareColor : appData.premoveHighlightColor, 0);
2513 DrawBorder (int x, int y, int type)
2515 DoDrawBorder(csBoardWindow, x, y, type);
2516 DoDrawBorder(csBoardBackup, x, y, type);
2520 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2522 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2523 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2525 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2526 if(textureW[kind] < W*squareSize)
2527 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2529 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2530 if(textureH[kind] < H*squareSize)
2531 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2533 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2538 DrawLogo (void *handle, void *logo)
2540 cairo_surface_t *img, *cs;
2544 if(!logo || !handle) return;
2545 cs = cairo_xlib_surface_create(xDisplay, XtWindow(handle), DefaultVisual(xDisplay, 0), appData.logoSize, appData.logoSize/2);
2546 img = cairo_image_surface_create_from_png (logo);
2547 w = cairo_image_surface_get_width (img);
2548 h = cairo_image_surface_get_height (img);
2549 cr = cairo_create(cs);
2550 cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
2551 cairo_set_source_surface (cr, img, 0, 0);
2554 cairo_surface_destroy (img);
2555 cairo_surface_destroy (cs);
2559 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2560 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2562 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2563 if(pngBoardBitmap[color]) {
2565 if(!fac && !cairoAnimate) return;
2567 cr = cairo_create (fac ? csBoardWindow : (cairo_surface_t *) dest);
2568 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2569 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2570 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2574 cr = cairo_create (csBoardBackup);
2575 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2576 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2577 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2582 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2583 squareSize, squareSize, x*fac, y*fac);
2585 if (useImages && useImageSqs) {
2589 pm = xpmLightSquare;
2594 case 2: /* neutral */
2596 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2599 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2600 squareSize, squareSize, x*fac, y*fac);
2610 case 2: /* neutral */
2615 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2620 I split out the routines to draw a piece so that I could
2621 make a generic flash routine.
2624 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2626 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2627 switch (square_color) {
2629 case 2: /* neutral */
2631 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2632 ? *pieceToOutline(piece)
2633 : *pieceToSolid(piece),
2634 dest, bwPieceGC, 0, 0,
2635 squareSize, squareSize, x, y);
2638 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2639 ? *pieceToSolid(piece)
2640 : *pieceToOutline(piece),
2641 dest, wbPieceGC, 0, 0,
2642 squareSize, squareSize, x, y);
2648 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2650 switch (square_color) {
2652 case 2: /* neutral */
2654 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2655 ? *pieceToOutline(piece)
2656 : *pieceToSolid(piece),
2657 dest, bwPieceGC, 0, 0,
2658 squareSize, squareSize, x, y, 1);
2661 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2662 ? *pieceToSolid(piece)
2663 : *pieceToOutline(piece),
2664 dest, wbPieceGC, 0, 0,
2665 squareSize, squareSize, x, y, 1);
2671 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2673 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2674 switch (square_color) {
2676 XCopyPlane(xDisplay, *pieceToSolid(piece),
2677 dest, (int) piece < (int) BlackPawn
2678 ? wlPieceGC : blPieceGC, 0, 0,
2679 squareSize, squareSize, x, y, 1);
2682 XCopyPlane(xDisplay, *pieceToSolid(piece),
2683 dest, (int) piece < (int) BlackPawn
2684 ? wdPieceGC : bdPieceGC, 0, 0,
2685 squareSize, squareSize, x, y, 1);
2687 case 2: /* neutral */
2689 break; // should never contain pieces
2694 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2696 int kind, p = piece;
2698 switch (square_color) {
2700 case 2: /* neutral */
2702 if ((int)piece < (int) BlackPawn) {
2710 if ((int)piece < (int) BlackPawn) {
2718 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2719 if(useTexture & square_color+1) {
2720 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2721 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2722 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2723 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2724 XSetClipMask(xDisplay, wlPieceGC, None);
2725 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2727 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2728 dest, wlPieceGC, 0, 0,
2729 squareSize, squareSize, x, y);
2733 pngDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2735 int kind, p = piece;
2738 if ((int)piece < (int) BlackPawn) {
2744 if(appData.upsideDown && flipView) { p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2745 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2747 cr = cairo_create (csBoardWindow);
2748 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2751 cr = cairo_create (csBoardBackup);
2752 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2757 typedef void (*DrawFunc)();
2762 if (appData.monoMode) {
2763 if (DefaultDepth(xDisplay, xScreen) == 1) {
2764 return monoDrawPiece_1bit;
2766 return monoDrawPiece;
2768 } else if(appData.pngDirectory[0]) {
2769 return pngDrawPiece;
2772 return colorDrawPieceImage;
2774 return colorDrawPiece;
2779 DoDrawDot (int marker, int x, int y, int r, cairo_surface_t *cs)
2783 cr = cairo_create(cs);
2784 cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
2785 if(appData.monoMode) {
2786 SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
2787 cairo_stroke_preserve(cr);
2788 SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
2790 SetPen(cr, 2, marker == 2 ? "#FF0000" : "#FFFF00", 0);
2799 DrawDot (int marker, int x, int y, int r)
2801 DoDrawDot(marker, x, y, r, csBoardWindow);
2802 DoDrawDot(marker, x, y, r, csBoardBackup);
2806 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2807 { // basic front-end board-draw function: takes care of everything that can be in square:
2808 // piece, background, coordinate/count, marker dot
2809 int direction, font_ascent, font_descent;
2810 XCharStruct overall;
2813 if (piece == EmptySquare) {
2814 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2816 drawfunc = ChooseDrawFunc();
2817 drawfunc(piece, square_color, x, y, xBoardWindow);
2820 if(align) { // square carries inscription (coord or piece count)
2822 GC hGC = align < 3 ? coordGC : countGC;
2823 // first calculate where it goes
2824 XTextExtents(countFontStruct, string, 1, &direction,
2825 &font_ascent, &font_descent, &overall);
2827 xx += squareSize - overall.width - 2;
2828 yy += squareSize - font_descent - 1;
2829 } else if (align == 2) {
2830 xx += 2, yy += font_ascent + 1;
2831 } else if (align == 3) {
2832 xx += squareSize - overall.width - 2;
2833 yy += font_ascent + 1;
2834 } else if (align == 4) {
2835 xx += 2, yy += font_ascent + 1;
2838 if (appData.monoMode) {
2839 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2841 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2845 if(marker) { // print fat marker dot, if requested
2846 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2851 FlashDelay (int flash_delay)
2853 XSync(xDisplay, False);
2854 if(flash_delay) do_flash_delay(flash_delay);
2858 Fraction (int x, int start, int stop)
2860 double f = ((double) x - start)/(stop - start);
2861 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2865 static WindowPlacement wpNew;
2868 CoDrag (Widget sh, WindowPlacement *wp)
2871 int j=0, touch=0, fudge = 2;
2872 GetActualPlacement(sh, wp);
2873 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2874 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2875 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2876 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2877 if(!touch ) return; // only windows that touch co-move
2878 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2879 int heightInc = wpNew.height - wpMain.height;
2880 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2881 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2882 wp->y += fracTop * heightInc;
2883 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2884 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2885 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2886 int widthInc = wpNew.width - wpMain.width;
2887 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2888 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2889 wp->y += fracLeft * widthInc;
2890 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2891 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2893 wp->x += wpNew.x - wpMain.x;
2894 wp->y += wpNew.y - wpMain.y;
2895 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2896 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2897 XtSetArg(args[j], XtNx, wp->x); j++;
2898 XtSetArg(args[j], XtNy, wp->y); j++;
2899 XtSetValues(sh, args, j);
2902 static XtIntervalId delayedDragID = 0;
2907 GetActualPlacement(shellWidget, &wpNew);
2908 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2909 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2910 return; // false alarm
2911 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2912 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2913 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2914 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2916 DrawPosition(True, NULL);
2917 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2924 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2926 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2930 EventProc (Widget widget, caddr_t unused, XEvent *event)
2932 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2933 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2936 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
2939 Color (char *col, int n)
2942 sscanf(col, "#%x", &c);
2948 SetPen (cairo_t *cr, float w, char *col, int dash)
2950 static const double dotted[] = {4.0, 4.0};
2951 static int len = sizeof(dotted) / sizeof(dotted[0]);
2952 cairo_set_line_width (cr, w);
2953 cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
2954 if(dash) cairo_set_dash (cr, dotted, len, 0.0);
2957 void DrawSeekAxis( int x, int y, int xTo, int yTo )
2962 cr = cairo_create (csBoardWindow);
2964 cairo_move_to (cr, x, y);
2965 cairo_line_to(cr, xTo, yTo );
2967 SetPen(cr, 2, "#000000", 0);
2974 void DrawSeekBackground( int left, int top, int right, int bottom )
2976 cairo_t *cr = cairo_create (csBoardWindow);
2978 cairo_rectangle (cr, left, top, right-left, bottom-top);
2980 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
2987 void DrawSeekText(char *buf, int x, int y)
2989 cairo_t *cr = cairo_create (csBoardWindow);
2991 cairo_select_font_face (cr, "Sans",
2992 CAIRO_FONT_SLANT_NORMAL,
2993 CAIRO_FONT_WEIGHT_NORMAL);
2995 cairo_set_font_size (cr, 12.0);
2997 cairo_move_to (cr, x, y+4);
2998 cairo_show_text( cr, buf);
3000 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
3007 void DrawSeekDot(int x, int y, int colorNr)
3009 cairo_t *cr = cairo_create (csBoardWindow);
3010 int square = colorNr & 0x80;
3014 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3016 cairo_arc(cr, x, y, squareSize/8, 0.0, 2*M_PI);
3018 SetPen(cr, 2, "#000000", 0);
3019 cairo_stroke_preserve(cr);
3021 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
3022 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
3023 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
3034 int boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3035 int boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3036 if(!csBoardWindow) {
3037 csBoardWindow = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), boardWidth, boardHeight);
3038 csBoardBackup = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, boardWidth, boardHeight);
3048 DoDrawGrid(cairo_surface_t *cs)
3050 /* draws a grid starting around Nx, Ny squares starting at x,y */
3056 cr = cairo_create (cs);
3058 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
3059 SetPen(cr, lineGap, "#000000", 0);
3062 for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
3064 cairo_move_to (cr, gridSegments[i].x1, gridSegments[i].y1);
3065 cairo_line_to (cr, gridSegments[i].x2, gridSegments[i].y2);
3078 DoDrawGrid(csBoardWindow);
3079 DoDrawGrid(csBoardBackup);
3083 * event handler for redrawing the board
3086 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3088 DrawPosition(True, NULL);
3093 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3094 { // [HGM] pv: walk PV
3095 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3098 static int savedIndex; /* gross that this is global */
3101 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3104 XawTextPosition index, dummy;
3107 XawTextGetSelectionPos(w, &index, &dummy);
3108 XtSetArg(arg, XtNstring, &val);
3109 XtGetValues(w, &arg, 1);
3110 ReplaceComment(savedIndex, val);
3111 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3112 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3116 EditCommentPopUp (int index, char *title, char *text)
3119 if (text == NULL) text = "";
3120 NewCommentPopup(title, text, index);
3124 CommentPopUp (char *title, char *text)
3126 savedIndex = currentMove; // [HGM] vari
3127 NewCommentPopup(title, text, currentMove);
3133 PopDown(CommentDlg);
3137 /* Disable all user input other than deleting the window */
3138 static int frozen = 0;
3144 /* Grab by a widget that doesn't accept input */
3145 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
3149 /* Undo a FreezeUI */
3153 if (!frozen) return;
3154 XtRemoveGrab(optList[W_MESSG].handle);
3162 static int oldPausing = FALSE;
3163 static GameMode oldmode = (GameMode) -1;
3166 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3168 if (pausing != oldPausing) {
3169 oldPausing = pausing;
3170 MarkMenuItem("Mode.Pause", pausing);
3172 if (appData.showButtonBar) {
3173 /* Always toggle, don't set. Previous code messes up when
3174 invoked while the button is pressed, as releasing it
3175 toggles the state again. */
3178 XtSetArg(args[0], XtNbackground, &oldbg);
3179 XtSetArg(args[1], XtNforeground, &oldfg);
3180 XtGetValues(optList[W_PAUSE].handle,
3182 XtSetArg(args[0], XtNbackground, oldfg);
3183 XtSetArg(args[1], XtNforeground, oldbg);
3185 XtSetValues(optList[W_PAUSE].handle, args, 2);
3189 wname = ModeToWidgetName(oldmode);
3190 if (wname != NULL) {
3191 MarkMenuItem(wname, False);
3193 wname = ModeToWidgetName(gameMode);
3194 if (wname != NULL) {
3195 MarkMenuItem(wname, True);
3198 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
3200 /* Maybe all the enables should be handled here, not just this one */
3201 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
3203 DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
3208 * Button/menu procedures
3211 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3212 char *selected_fen_position=NULL;
3215 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3216 Atom *type_return, XtPointer *value_return,
3217 unsigned long *length_return, int *format_return)
3219 char *selection_tmp;
3221 // if (!selected_fen_position) return False; /* should never happen */
3222 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3223 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3224 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3227 if (f == NULL) return False;
3231 selection_tmp = XtMalloc(len + 1);
3232 count = fread(selection_tmp, 1, len, f);
3235 XtFree(selection_tmp);
3238 selection_tmp[len] = NULLCHAR;
3240 /* note: since no XtSelectionDoneProc was registered, Xt will
3241 * automatically call XtFree on the value returned. So have to
3242 * make a copy of it allocated with XtMalloc */
3243 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3244 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3247 *value_return=selection_tmp;
3248 *length_return=strlen(selection_tmp);
3249 *type_return=*target;
3250 *format_return = 8; /* bits per byte */
3252 } else if (*target == XA_TARGETS(xDisplay)) {
3253 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3254 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3255 targets_tmp[1] = XA_STRING;
3256 *value_return = targets_tmp;
3257 *type_return = XA_ATOM;
3260 // This code leads to a read of value_return out of bounds on 64-bit systems.
3261 // Other code which I have seen always sets *format_return to 32 independent of
3262 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3263 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3264 *format_return = 8 * sizeof(Atom);
3265 if (*format_return > 32) {
3266 *length_return *= *format_return / 32;
3267 *format_return = 32;
3270 *format_return = 32;
3278 /* note: when called from menu all parameters are NULL, so no clue what the
3279 * Widget which was clicked on was, or what the click event was
3282 CopySomething (char *src)
3284 selected_fen_position = src;
3286 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3287 * have a notion of a position that is selected but not copied.
3288 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3290 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3292 SendPositionSelection,
3293 NULL/* lose_ownership_proc */ ,
3294 NULL/* transfer_done_proc */);
3295 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3297 SendPositionSelection,
3298 NULL/* lose_ownership_proc */ ,
3299 NULL/* transfer_done_proc */);
3302 /* function called when the data to Paste is ready */
3304 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3305 Atom *type, XtPointer value, unsigned long *len, int *format)
3308 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3309 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3310 EditPositionPasteFEN(fenstr);
3314 /* called when Paste Position button is pressed,
3315 * all parameters will be NULL */
3317 PastePositionProc ()
3319 XtGetSelectionValue(menuBarWidget,
3320 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3321 /* (XtSelectionCallbackProc) */ PastePositionCB,
3322 NULL, /* client_data passed to PastePositionCB */
3324 /* better to use the time field from the event that triggered the
3325 * call to this function, but that isn't trivial to get
3332 /* note: when called from menu all parameters are NULL, so no clue what the
3333 * Widget which was clicked on was, or what the click event was
3335 /* function called when the data to Paste is ready */
3337 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3338 Atom *type, XtPointer value, unsigned long *len, int *format)
3341 if (value == NULL || *len == 0) {
3342 return; /* nothing had been selected to copy */
3344 f = fopen(gamePasteFilename, "w");
3346 DisplayError(_("Can't open temp file"), errno);
3349 fwrite(value, 1, *len, f);
3352 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3355 /* called when Paste Game button is pressed,
3356 * all parameters will be NULL */
3360 XtGetSelectionValue(menuBarWidget,
3361 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3362 /* (XtSelectionCallbackProc) */ PasteGameCB,
3363 NULL, /* client_data passed to PasteGameCB */
3365 /* better to use the time field from the event that triggered the
3366 * call to this function, but that isn't trivial to get
3375 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3382 { // bassic primitive for determining if modifier keys are pressed
3383 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3386 XQueryKeymap(xDisplay,keys);
3387 for(i=0; i<6; i++) {
3389 j = XKeysymToKeycode(xDisplay, codes[i]);
3390 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3396 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3400 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3401 if ( n == 1 && *buf >= 32 // printable
3402 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3403 ) BoxAutoPopUp (buf);
3407 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3408 { // [HGM] input: let up-arrow recall previous line from history
3413 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3414 { // [HGM] input: let down-arrow recall next line from history
3419 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3425 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3427 if (!TempBackwardActive) {
3428 TempBackwardActive = True;
3434 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3436 /* Check to see if triggered by a key release event for a repeating key.
3437 * If so the next queued event will be a key press of the same key at the same time */
3438 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3440 XPeekEvent(xDisplay, &next);
3441 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3442 next.xkey.keycode == event->xkey.keycode)
3446 TempBackwardActive = False;
3450 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3451 { // called as key binding
3454 if (nprms && *nprms > 0)
3458 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3464 { // called from menu
3465 ManInner(NULL, NULL, NULL, NULL);
3469 SetWindowTitle (char *text, char *title, char *icon)
3473 if (appData.titleInWindow) {
3475 XtSetArg(args[i], XtNlabel, text); i++;
3476 XtSetValues(titleWidget, args, i);
3479 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3480 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3481 XtSetValues(shellWidget, args, i);
3482 XSync(xDisplay, False);
3487 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3493 DisplayIcsInteractionTitle (String message)
3495 if (oldICSInteractionTitle == NULL) {
3496 /* Magic to find the old window title, adapted from vim */
3497 char *wina = getenv("WINDOWID");
3499 Window win = (Window) atoi(wina);
3500 Window root, parent, *children;
3501 unsigned int nchildren;
3502 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3504 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3505 if (!XQueryTree(xDisplay, win, &root, &parent,
3506 &children, &nchildren)) break;
3507 if (children) XFree((void *)children);
3508 if (parent == root || parent == 0) break;
3511 XSetErrorHandler(oldHandler);
3513 if (oldICSInteractionTitle == NULL) {
3514 oldICSInteractionTitle = "xterm";
3517 printf("\033]0;%s\007", message);
3522 XtIntervalId delayedEventTimerXID = 0;
3523 DelayedEventCallback delayedEventCallback = 0;
3528 delayedEventTimerXID = 0;
3529 delayedEventCallback();
3533 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3535 if(delayedEventTimerXID && delayedEventCallback == cb)
3536 // [HGM] alive: replace, rather than add or flush identical event
3537 XtRemoveTimeOut(delayedEventTimerXID);
3538 delayedEventCallback = cb;
3539 delayedEventTimerXID =
3540 XtAppAddTimeOut(appContext, millisec,
3541 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3544 DelayedEventCallback
3547 if (delayedEventTimerXID) {
3548 return delayedEventCallback;
3555 CancelDelayedEvent ()
3557 if (delayedEventTimerXID) {
3558 XtRemoveTimeOut(delayedEventTimerXID);
3559 delayedEventTimerXID = 0;
3563 XtIntervalId loadGameTimerXID = 0;
3566 LoadGameTimerRunning ()
3568 return loadGameTimerXID != 0;
3572 StopLoadGameTimer ()
3574 if (loadGameTimerXID != 0) {
3575 XtRemoveTimeOut(loadGameTimerXID);
3576 loadGameTimerXID = 0;
3584 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3586 loadGameTimerXID = 0;
3591 StartLoadGameTimer (long millisec)
3594 XtAppAddTimeOut(appContext, millisec,
3595 (XtTimerCallbackProc) LoadGameTimerCallback,
3599 XtIntervalId analysisClockXID = 0;
3602 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3604 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3605 || appData.icsEngineAnalyze) { // [DM]
3606 AnalysisPeriodicEvent(0);
3607 StartAnalysisClock();
3612 StartAnalysisClock ()
3615 XtAppAddTimeOut(appContext, 2000,
3616 (XtTimerCallbackProc) AnalysisClockCallback,
3620 XtIntervalId clockTimerXID = 0;
3623 ClockTimerRunning ()
3625 return clockTimerXID != 0;
3631 if (clockTimerXID != 0) {
3632 XtRemoveTimeOut(clockTimerXID);
3641 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3648 StartClockTimer (long millisec)
3651 XtAppAddTimeOut(appContext, millisec,
3652 (XtTimerCallbackProc) ClockTimerCallback,
3657 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3661 Widget w = (Widget) opt->handle;
3663 /* check for low time warning */
3664 Pixel foregroundOrWarningColor = timerForegroundPixel;
3667 appData.lowTimeWarning &&
3668 (timer / 1000) < appData.icsAlarmTime)
3669 foregroundOrWarningColor = lowTimeWarningColor;
3671 if (appData.clockMode) {
3672 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
3673 XtSetArg(args[0], XtNlabel, buf);
3675 snprintf(buf, MSG_SIZ, "%s ", color);
3676 XtSetArg(args[0], XtNlabel, buf);
3681 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3682 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3684 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3685 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3688 XtSetValues(w, args, 3);
3691 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3694 SetClockIcon (int color)
3697 Pixmap pm = *clockIcons[color];
3698 if (iconPixmap != pm) {
3700 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3701 XtSetValues(shellWidget, args, 1);
3706 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3708 InputSource *is = (InputSource *) closure;
3713 if (is->lineByLine) {
3714 count = read(is->fd, is->unused,
3715 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3717 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3720 is->unused += count;
3722 while (p < is->unused) {
3723 q = memchr(p, '\n', is->unused - p);
3724 if (q == NULL) break;
3726 (is->func)(is, is->closure, p, q - p, 0);
3730 while (p < is->unused) {
3735 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3740 (is->func)(is, is->closure, is->buf, count, error);
3745 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3748 ChildProc *cp = (ChildProc *) pr;
3750 is = (InputSource *) calloc(1, sizeof(InputSource));
3751 is->lineByLine = lineByLine;
3755 is->fd = fileno(stdin);
3757 is->kind = cp->kind;
3758 is->fd = cp->fdFrom;
3761 is->unused = is->buf;
3764 is->xid = XtAppAddInput(appContext, is->fd,
3765 (XtPointer) (XtInputReadMask),
3766 (XtInputCallbackProc) DoInputCallback,
3768 is->closure = closure;
3769 return (InputSourceRef) is;
3773 RemoveInputSource (InputSourceRef isr)
3775 InputSource *is = (InputSource *) isr;
3777 if (is->xid == 0) return;
3778 XtRemoveInput(is->xid);
3782 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3784 /* Masks for XPM pieces. Black and white pieces can have
3785 different shapes, but in the interest of retaining my
3786 sanity pieces must have the same outline on both light
3787 and dark squares, and all pieces must use the same
3788 background square colors/images. */
3790 static int xpmDone = 0;
3791 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3792 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3793 static cairo_surface_t *c_animBufs[3*NrOfAnims]; // newBuf, saveBuf
3796 CreateAnimMasks (int pieceDepth)
3802 unsigned long plane;
3805 /* Need a bitmap just to get a GC with right depth */
3806 buf = XCreatePixmap(xDisplay, xBoardWindow,
3808 values.foreground = 1;
3809 values.background = 0;
3810 /* Don't use XtGetGC, not read only */
3811 maskGC = XCreateGC(xDisplay, buf,
3812 GCForeground | GCBackground, &values);
3813 XFreePixmap(xDisplay, buf);
3815 buf = XCreatePixmap(xDisplay, xBoardWindow,
3816 squareSize, squareSize, pieceDepth);
3817 values.foreground = XBlackPixel(xDisplay, xScreen);
3818 values.background = XWhitePixel(xDisplay, xScreen);
3819 bufGC = XCreateGC(xDisplay, buf,
3820 GCForeground | GCBackground, &values);
3822 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3823 /* Begin with empty mask */
3824 if(!xpmDone) // [HGM] pieces: keep using existing
3825 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3826 squareSize, squareSize, 1);
3827 XSetFunction(xDisplay, maskGC, GXclear);
3828 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3829 0, 0, squareSize, squareSize);
3831 /* Take a copy of the piece */
3836 XSetFunction(xDisplay, bufGC, GXcopy);
3837 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3839 0, 0, squareSize, squareSize, 0, 0);
3841 /* XOR the background (light) over the piece */
3842 XSetFunction(xDisplay, bufGC, GXxor);
3844 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3845 0, 0, squareSize, squareSize, 0, 0);
3847 XSetForeground(xDisplay, bufGC, lightSquareColor);
3848 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3851 /* We now have an inverted piece image with the background
3852 erased. Construct mask by just selecting all the non-zero
3853 pixels - no need to reconstruct the original image. */
3854 XSetFunction(xDisplay, maskGC, GXor);
3856 /* Might be quicker to download an XImage and create bitmap
3857 data from it rather than this N copies per piece, but it
3858 only takes a fraction of a second and there is a much
3859 longer delay for loading the pieces. */
3860 for (n = 0; n < pieceDepth; n ++) {
3861 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3862 0, 0, squareSize, squareSize,
3868 XFreePixmap(xDisplay, buf);
3869 XFreeGC(xDisplay, bufGC);
3870 XFreeGC(xDisplay, maskGC);
3874 InitAnimState (AnimNr anr, XWindowAttributes *info)
3880 DrawSeekOpen(); // set cs to board widget
3881 c_animBufs[anr+4] = csBoardWindow;
3882 c_animBufs[anr+2] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3883 c_animBufs[anr] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
3886 /* Each buffer is square size, same depth as window */
3887 animBufs[anr+4] = xBoardWindow;
3888 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3889 squareSize, squareSize, info->depth);
3890 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3891 squareSize, squareSize, info->depth);
3893 /* Create a plain GC for blitting */
3894 mask = GCForeground | GCBackground | GCFunction |
3895 GCPlaneMask | GCGraphicsExposures;
3896 values.foreground = XBlackPixel(xDisplay, xScreen);
3897 values.background = XWhitePixel(xDisplay, xScreen);
3898 values.function = GXcopy;
3899 values.plane_mask = AllPlanes;
3900 values.graphics_exposures = False;
3901 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3903 /* Piece will be copied from an existing context at
3904 the start of each new animation/drag. */
3905 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3907 /* Outline will be a read-only copy of an existing */
3908 animGCs[anr+4] = None;
3914 XWindowAttributes info;
3916 if (xpmDone && gameInfo.variant == oldVariant) return;
3917 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3918 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3920 InitAnimState(Game, &info);
3921 InitAnimState(Player, &info);
3923 /* For XPM pieces, we need bitmaps to use as masks. */
3925 CreateAnimMasks(info.depth), xpmDone = 1;
3930 static Boolean frameWaiting;
3933 FrameAlarm (int sig)
3935 frameWaiting = False;
3936 /* In case System-V style signals. Needed?? */
3937 signal(SIGALRM, FrameAlarm);
3941 FrameDelay (int time)
3943 struct itimerval delay;
3945 XSync(xDisplay, False);
3948 frameWaiting = True;
3949 signal(SIGALRM, FrameAlarm);
3950 delay.it_interval.tv_sec =
3951 delay.it_value.tv_sec = time / 1000;
3952 delay.it_interval.tv_usec =
3953 delay.it_value.tv_usec = (time % 1000) * 1000;
3954 setitimer(ITIMER_REAL, &delay, NULL);
3955 while (frameWaiting) pause();
3956 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3957 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3958 setitimer(ITIMER_REAL, &delay, NULL);
3965 FrameDelay (int time)
3967 XSync(xDisplay, False);
3969 usleep(time * 1000);
3975 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3979 /* Bitmap for piece being moved. */
3980 if (appData.monoMode) {
3981 *mask = *pieceToSolid(piece);
3982 } else if (useImages) {
3984 *mask = xpmMask[piece];
3986 *mask = ximMaskPm[piece];
3989 *mask = *pieceToSolid(piece);
3992 /* GC for piece being moved. Square color doesn't matter, but
3993 since it gets modified we make a copy of the original. */
3995 if (appData.monoMode)
4000 if (appData.monoMode)
4005 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
4007 /* Outline only used in mono mode and is not modified */
4009 *outline = bwPieceGC;
4011 *outline = wbPieceGC;
4015 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
4020 /* Draw solid rectangle which will be clipped to shape of piece */
4021 XFillRectangle(xDisplay, dest, clip,
4022 0, 0, squareSize, squareSize);
4023 if (appData.monoMode)
4024 /* Also draw outline in contrasting color for black
4025 on black / white on white cases */
4026 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
4027 0, 0, squareSize, squareSize, 0, 0, 1);
4029 /* Copy the piece */
4034 if(appData.upsideDown && flipView) kind ^= 2;
4035 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4037 0, 0, squareSize, squareSize,
4043 CairoOverlayPiece (ChessSquare piece, cairo_surface_t *dest)
4045 static ChessSquare oldPiece = -1;
4046 static cairo_t *pieceSource;
4047 if(piece != oldPiece) { // try make it faster by only changing cr if we need other piece
4048 if(pieceSource) cairo_destroy (pieceSource);
4049 pieceSource = cairo_create (dest);
4050 cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
4053 cairo_paint(pieceSource);
4057 InsertPiece (AnimNr anr, ChessSquare piece)
4060 CairoOverlayPiece(piece, c_animBufs[anr]);
4062 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
4066 DrawBlank (AnimNr anr, int x, int y, int startColor)
4069 BlankSquare(x, y, startColor, EmptySquare, (Drawable) c_animBufs[anr+2], 0);
4071 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
4074 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
4075 int srcX, int srcY, int width, int height, int destX, int destY)
4078 cairo_t *cr;// = cairo_create (c_animBufs[anr+destBuf]);
4079 cr = cairo_create (c_animBufs[anr+destBuf]);
4080 if(c_animBufs[anr+srcBuf] == csBoardWindow)
4081 cairo_set_source_surface (cr, csBoardBackup, destX - srcX, destY - srcY);
4083 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
4084 cairo_rectangle (cr, destX, destY, width, height);
4087 if(c_animBufs[anr+destBuf] == csBoardWindow) {
4088 cr = cairo_create (csBoardBackup); // also draw to backup
4089 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
4090 cairo_rectangle (cr, destX, destY, width, height);
4095 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
4096 srcX, srcY, width, height, destX, destY);
4100 SetDragPiece (AnimNr anr, ChessSquare piece)
4103 if(cairoAnimate) return;
4104 /* The piece will be drawn using its own bitmap as a matte */
4105 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
4106 XSetClipMask(xDisplay, animGCs[anr+2], mask);
4109 /* [AS] Arrow highlighting support */
4111 void DrawPolygon(Pnt arrow[], int nr)
4112 { // for now on own surface; eventually this should become a global that is only destroyed on resize
4113 cairo_surface_t *boardSurface;
4116 int w = lineGap + BOARD_WIDTH * (squareSize + lineGap);
4117 int h = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
4118 boardSurface = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), w, h);
4119 cr = cairo_create (boardSurface);
4120 cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
4121 for (i=0;i<nr;i++) {
4122 cairo_line_to(cr, arrow[i].x, arrow[i].y);
4124 if(appData.monoMode) { // should we always outline arrow?
4125 cairo_line_to(cr, arrow[0].x, arrow[0].y);
4126 SetPen(cr, 2, "#000000", 0);
4127 cairo_stroke_preserve(cr);
4129 SetPen(cr, 2, appData.highlightSquareColor, 0);
4134 cairo_surface_destroy (boardSurface);
4138 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
4140 char buf[MSG_SIZ], *logoName = buf;
4141 if(appData.logo[n][0]) {
4142 logoName = appData.logo[n];
4143 } else if(appData.autoLogo) {
4144 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
4145 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
4146 } else if(appData.directory[n] && appData.directory[n][0]) {
4147 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
4151 { ASSIGN(cps->programLogo, logoName); }
4155 UpdateLogos (int displ)
4157 if(optList[W_WHITE-1].handle == NULL) return;
4158 LoadLogo(&first, 0, 0);
4159 LoadLogo(&second, 1, appData.icsActive);
4160 if(displ) DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);